From c9cf57c933fe6a2cbc85c71786412ebd2670ef99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 6 Jul 2023 10:42:30 +0200 Subject: [PATCH 01/57] feat(poc): react nextjs initial --- .env.example | 12 +- README.md | 234 +- app/[page]/opengraph-image.tsx | 4 +- app/[page]/page.tsx | 8 +- app/layout.tsx | 2 +- app/page.tsx | 2 +- .../{[handle] => [...handle]}/page.tsx | 5 +- .../[...collection]}/page.tsx | 9 +- .../opengraph-image.tsx | 2 +- app/search/page.tsx | 4 +- components/carousel.tsx | 64 +- components/cart/index.tsx | 31 +- components/grid/three-items.tsx | 8 +- components/layout/footer.tsx | 22 +- components/layout/navbar/index.tsx | 12 +- components/layout/product-grid-items.tsx | 6 +- components/layout/search/collections.tsx | 10 +- lib/shopware/api-types/apiTypes-6.5.2.0.d.ts | 7733 +++++++++++++++++ lib/shopware/api.ts | 111 + lib/shopware/criteria.ts | 245 + lib/shopware/index.ts | 199 + lib/shopware/transform.ts | 203 + lib/shopware/types.ts | 96 + next.config.js | 4 +- package.json | 21 +- pnpm-lock.yaml | 619 +- tsconfig.json | 2 +- 27 files changed, 9010 insertions(+), 658 deletions(-) rename app/product/{[handle] => [...handle]}/page.tsx (94%) rename app/search/{[collection] => (collection)/[...collection]}/page.tsx (87%) rename app/search/{[collection] => (collection)}/opengraph-image.tsx (88%) create mode 100644 lib/shopware/api-types/apiTypes-6.5.2.0.d.ts create mode 100644 lib/shopware/api.ts create mode 100644 lib/shopware/criteria.ts create mode 100644 lib/shopware/index.ts create mode 100644 lib/shopware/transform.ts create mode 100644 lib/shopware/types.ts diff --git a/.env.example b/.env.example index dc6582eb5..a4666dd31 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ -TWITTER_CREATOR="@vercel" -TWITTER_SITE="https://nextjs.org/commerce" -SITE_NAME="Next.js Commerce" -SHOPIFY_REVALIDATION_SECRET= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= -SHOPIFY_STORE_DOMAIN= +TWITTER_CREATOR="@shopware" +TWITTER_SITE="https://www.shopware.com/en/solutions/shopware-composable-frontends/" +SITE_NAME="Next.js Commerce with Shopware Composable Frontends" +SHOPWARE_STORE_DOMAIN="" +SHOPWARE_API_TYPE="store-api" +SHOPWARE_ACCESS_TOKEN="" diff --git a/README.md b/README.md index 07b281de7..aefe269f4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&env=SHOPIFY_REVALIDATION_SECRET,SHOPIFY_STOREFRONT_ACCESS_TOKEN,SHOPIFY_STORE_DOMAIN,SITE_NAME,TWITTER_CREATOR,TWITTER_SITE) - -# Next.js Commerce +# Next.js Commerce & Shopware Composable Frontends A Next.js 13 and App Router-ready ecommerce template featuring: @@ -12,27 +10,21 @@ A Next.js 13 and App Router-ready ecommerce template featuring: - New fetching and caching paradigms - Dynamic OG images - Styling with Tailwind CSS -- Checkout and payments with Shopify - Automatic light/dark mode based on system settings > Note: Looking for Next.js Commerce v1? View the [code](https://github.com/vercel/commerce/tree/v1), [demo](https://commerce-v1.vercel.store), and [release notes](https://github.com/vercel/commerce/releases/tag/v1) -## Providers +## Prerequisites -Vercel will only be actively maintaining a Shopify version [as outlined in our vision and strategy for Next.js Commerce](https://github.com/vercel/commerce/pull/966). +Next.js + Shopware requires a running [Shopware 6 Instance (Installation Guide)](https://developer.shopware.com/docs/guides/installation). -Vercel is more than happy to partner and work with any commerce provider to help them get a similar template up and running and listed below. Alternative providers should be able to fork this repository and swap out the `lib/shopify` file with their own implementation while leaving the rest of the template mostly unchanged. - -- Shopify (this repository) -- [BigCommerce](https://github.com/bigcommerce/nextjs-commerce) ([Demo](https://next-commerce-v2.vercel.app/)) -- [Medusa](https://github.com/medusajs/vercel-commerce) ([Demo](https://medusa-nextjs-commerce.vercel.app/)) -- [Saleor](https://github.com/saleor/nextjs-commerce) ([Demo](https://saleor-commerce.vercel.app/)) +To get started, use this README and the example environment variable comments. ## Running locally You will need to use the environment variables [defined in `.env.example`](.env.example) to run Next.js Commerce. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables) for this, but a `.env` file is all that is necessary. -> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control your Shopify store. +> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control your Shopware store. 1. Install Vercel CLI: `npm i -g vercel` 2. Link local instance with Vercel and GitHub accounts (creates `.vercel` directory): `vercel link` @@ -45,220 +37,24 @@ pnpm dev Your app should now be running on [localhost:3000](http://localhost:3000/). -## How to configure your Shopify store for Next.js Commerce +## How to configure your Shopware store for Next.js Commerce -Next.js Commerce requires a [paid Shopify plan](https://www.shopify.com/pricing). +You can find the `SHOPWARE_ACCESS_TOKEN` in the Admin at your SalesChannel configuration. -> Note: Next.js Commerce will not work with a Shopify Starter plan as it does not allow installation of custom themes, which is required to run as a headless storefront. +### Add Shopware domain to an environment variable -### Add Shopify domain to an environment variable +Copy `.env.example` file to `.env` and change the environment variables like descibed below. -Create a `SHOPIFY_STORE_DOMAIN` environment variable and use your Shopify domain as the the value (ie. `SHOPIFY_STORE_SUBDOMAIN.myshopify.com`). +Create a `SHOPWARE_STORE_DOMAIN` environment variable and use your Shopware domain as the the value (ie. `demo-frontends.shopware.store`). > Note: Do not include the `https://`. -### Accessing the Shopify Storefront API +### Accessing the Shopware store API -Next.js Commerce utilizes [Shopify's Storefront API](https://shopify.dev/docs/api/storefront) to create unique customer experiences. The API offers a full range of commerce options making it possible for customers to control products, collections, menus, pages, cart, checkout, and more. +Next.js Commerce utilizes [Shopware's store API](https://shopware.stoplight.io/docs/store-api/) to create unique customer experiences. The API offers a full range of commerce options making it possible for customers to control products, collections, menus, pages, cart, checkout, and more. -In order to use the Shopify's Storefront API, you need to install the [Headless app](https://apps.shopify.com/headless) in your Shopify store. +In order to use the Shopware's store API, you need at least one _(Storefront)_ SalesChannel _(for speaking URL's)_ in your Shopware instance. -Once installed, you'll need to create a `SHOPIFY_STOREFRONT_ACCESS_TOKEN` environment variable and use the public access token as the value +Once installed, you'll need to create a `SHOPWARE_ACCESS_TOKEN` environment variable and use the public access token as the value -> Note: Shopify does offer a Node.js Storefront API SDK. We use the Storefront API via GraphQL directly instead of the Node.js SDK so we have more control over fetching and caching. - -
- Expand to view detailed walkthrough - -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/settings/apps`. -1. Click the green `Shopify App Store` button. - ![Shopify App Store](https://user-images.githubusercontent.com/446260/233220545-cb4c1461-ebc5-424e-a421-bf0d32044027.jpg) -1. Search for `Headless` and click on the `Headless` app. - ![Headless](https://user-images.githubusercontent.com/446260/233220547-6d93b5ef-16c7-45db-99e7-13ae7e18eb39.jpg) -1. Click the black `Add app` button. - ![Add app](https://user-images.githubusercontent.com/446260/233220550-a34c8bda-75a8-437a-9673-125f3794ff35.jpg) -1. Click the green `Add sales channel` button. - ![Add sales channel](https://user-images.githubusercontent.com/446260/233220553-42d94a74-421d-4f8a-99ab-a95936b707a3.jpg) -1. Click the green `Create storefront` button. - ![Create storefront](https://user-images.githubusercontent.com/446260/233220556-1eee15c4-a45d-446e-9f73-2e7c9f56b29c.jpg) -1. Copy and paste the public access token and assign it to a `SHOPIFY_STOREFRONT_ACCESS_TOKEN` environment variable. - ![Pubic access token](https://user-images.githubusercontent.com/446260/233220558-5db04ff9-b894-40fe-bfba-0e92f26b8e1f.jpg) -1. If you ever need to reference the public access token again, you can navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/headless_storefronts`. -
- -### Install a headless theme - -When using a headless Shopify setup, you normally don't want customers to access any of the theme pages except for checkout. However, you can't totally disable the theme and a lot of links will still point to the theme (e.g. links in emails, order details, plugins, checkout, etc.). - -To enable a seamless flow between your headless site and Shopify, you can install the [Shopify Headless Theme](https://github.com/instantcommerce/shopify-headless-theme). - -Follow the installation instructions and configure the theme with your headless site's values. - -
- Expand to view detailed walkthrough - -1. Download [Shopify Headless Theme](https://github.com/instantcommerce/shopify-headless-theme). - ![Download Shoify Headless Theme](https://user-images.githubusercontent.com/446260/233220560-9f3f5ab0-ffb4-4305-b4ee-2c9d33eea90f.jpg) -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/themes`. -1. Click `Add theme`, then `Upload zip file`. - ![Upload zip file](https://user-images.githubusercontent.com/446260/233220561-7a53809e-0d95-45eb-b52f-3a52e3663a9c.jpg) -1. Select the downloaded zip file from above, and click the green `Upload file` button. - ![Select and upload file](https://user-images.githubusercontent.com/446260/233220563-135fb9f7-2921-4189-8f17-3b1cc15c0ea6.jpg) -1. Click `Customize`. - ![Customize theme](https://user-images.githubusercontent.com/446260/233220565-24b9c954-c18a-46f1-9db5-3d2a00040e48.jpg) -1. Click `Theme settings` (ie. the paintbrush icon), expand the `STOREFRONT` section, enter your headless store domain, click the gray `Publish` button. - ![Set headless domain in theme settings](https://user-images.githubusercontent.com/446260/233220566-acaee14d-03f8-400d-a2a2-28e85eb5ecdc.jpg) -1. Confirm the theme change by clicking the green `Save and publish` button. - ![Confirm save and publish](https://user-images.githubusercontent.com/446260/233220567-504d5bde-cfb9-426d-a264-f9a12d02af13.jpg) -1. The headless theme should now be your current active theme. -![Headless theme is current and active](https://user-images.githubusercontent.com/446260/233220569-63cab2b4-241b-4bf1-9b5b-451daaeceb91.jpg) -
- -### Branding & Design - -Since you're creating a headless Shopify store, you'll be in full control of your brand and design. However, there are still a few aspects we're leaving within Shopify's control. - -- Checkout -- Emails -- Order status -- Order history -- Favicon (for any Shopify controlled pages) - -You can use Shopify's admin to customize these pages to match your brand and design. - -
- Expand to view detailed walkthrough - -#### Checkout, order status, and order history - -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/settings/checkout`. -1. Click the green `Customize` button. - ![Customize](https://user-images.githubusercontent.com/446260/233220530-9beda4b4-5008-440a-b923-9d196b722539.jpg) -1. Click `Branding` (ie. the paintbrush icon) and customize your brand. Please note, there are three steps / pages to the checkout flow. Use the dropdown to change pages and adjust branding as needed on each page. Click `Save` when you are done. - ![Branding](https://user-images.githubusercontent.com/446260/233220534-e884d9fd-1a39-4f4d-9d09-163dde47c2e8.jpg) -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/settings/branding`. -1. Customize settings to match your brand. - ![Branding](https://user-images.githubusercontent.com/446260/233220536-452b8802-9a1e-40f0-9a12-52b3dace84a5.jpg) - -#### Emails - -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/settings/email_settings`. -1. Customize settings to match your brand. - ![Branding](https://user-images.githubusercontent.com/446260/233220538-13c83a9e-55f8-41e6-9b34-a39ee0848a8a.jpg) - -#### Favicon - -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/themes`. -1. Click the green `Customize` button. - ![Customize theme](https://user-images.githubusercontent.com/446260/233220539-4869a6cd-f59f-4de6-8091-95ed81d2302d.jpg) -1. Click `Theme settings` (ie. the paintbrush icon), expand the `FAVICON` section, upload favicon, then click the `Save` button. - ![Favicon](https://user-images.githubusercontent.com/446260/233220542-ac81b674-d86e-4172-ab38-c79d1ad1ff36.jpg) - -
- -### Configure webhooks for on-demand incremental static regeneration (ISR) - -Utilizing [Shopify's webhooks](https://shopify.dev/docs/apps/webhooks), and listening for select [Shopify webhook event topics](https://shopify.dev/docs/api/admin-rest/2022-04/resources/webhook#event-topics), we can use [Next'js on-demand revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#using-on-demand-revalidation) to keep data fetches indefinitely cached until certain events in the Shopify store occur. - -Next.js is pre-configured to listen for the following Shopify webhook events and automatically revalidate fetches. - -- `collections/create` -- `collections/delete` -- `collections/update` -- `products/create` -- `products/delete` -- `products/update` (this also includes when variants are added, updated, and removed as well as when products are purchased so inventory and out of stocks can be updated) - -
- Expand to view detailed walkthrough - -#### Setup secret for secure revalidation - -1. Create your own secret or [generate a random UUID](https://www.uuidgenerator.net/guid). -1. Create a [Vercel Environment Variable](https://vercel.com/docs/concepts/projects/environment-variables) named `SHOPIFY_REVALIDATION_SECRET` and use the value from above. - -#### Configure Shopify webhooks - -1. Navigate to `https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/settings/notifications`. -1. Add webhooks for all six event topics listed above. You can add more sets for other preview urls, environments, or local development. Append `?secret=[SECRET]` to each url, where `[SECRET]` is the secret you created above. - ![Shopify store webhooks](https://github.com/vercel/commerce/assets/446260/3d713fd7-b642-46e2-b2ce-f2b695ff6d2b) - ![Shopify store add webhook](https://github.com/vercel/commerce/assets/446260/f0240a22-be07-42bc-bf6c-b97873868677) - -#### Testing webhooks during local development - -The easiest way to test webhooks while developing locally is to use [ngrok](https://ngrok.com). - -1. [Install and configure ngrok](https://ngrok.com/download) (you will need to create an account). -1. Run your app locally, `npm run dev`. -1. In a separate terminal session, run `ngrok http 3000`. -1. Use the url generated by ngrok and add or update your webhook urls in Shopify. - ![ngrok](https://github.com/vercel/commerce/assets/446260/5dc09c5d-0e48-479c-ab64-de8dc9a2c4b1) - ![Shopify store edit webhook](https://github.com/vercel/commerce/assets/446260/13fd397d-4666-4e8d-b25f-4adc674345c0) -1. You can now make changes to your store and your local app should receive updates. You can also use the `Send test notification` button to trigger a generic webhook test. - ![Shopify store webhook send test notification](https://github.com/vercel/commerce/assets/446260/e872e233-1663-446d-961f-8c9455358530) - -
- -### Using Shopify as a CMS - -Next.js Commerce is fully powered by Shopify in a truly headless and data driven way. - -#### Products - -`https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/products` - -Only `Active` products are shown. `Draft` products will not be shown until they are marked as `Active`. - -`Active` products can still be hidden and not seen by navigating the site, by adding a `nextjs-frontend-hidden` tag on the product. This tag will also tell search engines to not index or crawl the product. The product is still directly accessible via url. This feature is great for "secret" products you only want to people you share the url with. - -Product options and option combinations are driven from Shopify options and variants. When selecting options on the product detail page, other option and variant combinations will be visually validated and verified for availability, like Amazon does. - -Products that are active and "out of stock" are still shown on the site, but the ability to add the product to the cart is disabled. - -#### Collections - -`https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/collections` - -Create whatever collections you want and configure them however you want. All available collections will show on the search page as filters on the left, with one exception... - -Any collection names that start with the word "hidden" will not show up on the headless front end. The Next.js Commerce theme comes pre-configured to look for two hidden collections. Collections were chosen for this over tags so that order of products could be controlled (collections allow for manual ordering). - -Create the following collections: - -- `Hidden: Homepage Featured Items` -- Products in this collection are displayed in the three featured blocks on the homepage. -- `Hidden: Homepage Carousel` -- Products in this collection are displayed in the auto-scrolling carousel section on the homepage. - -![Shopify collections](https://user-images.githubusercontent.com/446260/233220543-81896a2b-7085-4abc-a4f1-ce321e08b953.jpg) - -![Shopify collection detail](https://user-images.githubusercontent.com/446260/233220544-ecd4c069-49fc-4a0b-8378-aa5e1b4b5257.jpg) - -#### Pages - -`https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/pages` - -Next.js Commerce contains a dynamic `[page]` route. It will use the value to look for a corresponding page in Shopify. If a page is found, it will display its rich content using Tailwind's prose. If a page is not found, a 404 page is displayed. - -![Shopify pages](https://user-images.githubusercontent.com/446260/233221142-4dc3fa56-5256-4d84-b0a3-331ffb7d79b2.jpg) - -![Shopify page detail](https://user-images.githubusercontent.com/446260/233247700-cbeaf917-fb67-49e9-b9b9-5ee8cb188639.jpg) - -#### Navigation menus - -`https://SHOPIFY_STORE_SUBDOMAIN.myshopify.com/admin/menus` - -Next.js Commerce's header and footer navigation is pre-configured to be controlled by Shopify navigation menus. This means you have full control over what links go here. They can be to collections, pages, external links, and more. - -Create the following navigation menus: - -- `Next.js Frontend Header Menu` -- Menu items to be shown in the headless frontend header. -- `Next.js Frontend Footer Menu` -- Menu items to be shown in the headless frontend footer. - -![Shopify navigation menus](https://user-images.githubusercontent.com/446260/233220571-33f9d5a8-1206-4ab4-ad79-83b4ca954331.jpg) - -![Shopify navigation menu detail](https://user-images.githubusercontent.com/446260/233220573-5f03a51f-4100-461f-a696-f085856e391b.jpg) - -#### SEO - -Shopify's products, collections, pages, etc. allow you to create custom SEO titles and descriptions. Next.js Commerce is pre-configured to display these custom values, but also comes with sensible default fallbacks if they are not provided. - -![Shopify SEO](https://user-images.githubusercontent.com/446260/233247701-0ff2a560-7949-4e6c-b3a8-8168ed6341f8.jpg) +> Note: We using an [api Client package](https://www.npmjs.com/package/@shopware/api-client) that helps you with types, endpoints, params and returns. diff --git a/app/[page]/opengraph-image.tsx b/app/[page]/opengraph-image.tsx index 2fd59281e..db45917a7 100644 --- a/app/[page]/opengraph-image.tsx +++ b/app/[page]/opengraph-image.tsx @@ -1,11 +1,11 @@ import OpengraphImage from 'components/opengraph-image'; -import { getPage } from 'lib/shopify'; +import { getPage } from 'lib/shopware'; export const runtime = 'edge'; export default async function Image({ params }: { params: { page: string } }) { const page = await getPage(params.page); - const title = page.seo?.title || page.title; + const title = page ? page.seo?.title || page.title : ''; return await OpengraphImage({ title }); } diff --git a/app/[page]/page.tsx b/app/[page]/page.tsx index 7e0defed3..52eefdb06 100644 --- a/app/[page]/page.tsx +++ b/app/[page]/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next'; import Prose from 'components/prose'; -import { getPage } from 'lib/shopify'; +import { getPage } from 'lib/shopware'; import { notFound } from 'next/navigation'; export const runtime = 'edge'; @@ -39,6 +39,10 @@ export default async function Page({ params }: { params: { page: string } }) { const page = await getPage(params.page); if (!page) return notFound(); + let date = page.createdAt; + if (page.updatedAt !== '') { + date = page.updatedAt; + } return ( <> @@ -49,7 +53,7 @@ export default async function Page({ params }: { params: { page: string } }) { year: 'numeric', month: 'long', day: 'numeric' - }).format(new Date(page.updatedAt))}.`} + }).format(new Date(date))}.`}

); diff --git a/app/layout.tsx b/app/layout.tsx index dca2c624f..b93239710 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -7,7 +7,7 @@ const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env; export const metadata = { title: { - default: SITE_NAME, + default: SITE_NAME || 'Shopware Composable Frontends', template: `%s | ${SITE_NAME}` }, robots: { diff --git a/app/page.tsx b/app/page.tsx index 991a7345e..5f357726a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,7 +6,7 @@ import { Suspense } from 'react'; export const runtime = 'edge'; export const metadata = { - description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopify.', + description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopware.', openGraph: { images: [ { diff --git a/app/product/[handle]/page.tsx b/app/product/[...handle]/page.tsx similarity index 94% rename from app/product/[handle]/page.tsx rename to app/product/[...handle]/page.tsx index 45286cdbe..573fe475b 100644 --- a/app/product/[handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -10,8 +10,8 @@ import { Gallery } from 'components/product/gallery'; import { VariantSelector } from 'components/product/variant-selector'; import Prose from 'components/prose'; import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; -import { getProduct, getProductRecommendations } from 'lib/shopify'; -import { Image } from 'lib/shopify/types'; +import { getProduct, getProductRecommendations } from 'lib/shopware'; +import { Image } from 'lib/shopware/types'; export const runtime = 'edge'; @@ -20,6 +20,7 @@ export async function generateMetadata({ }: { params: { handle: string }; }): Promise { + // @ToDo: create a simpler function and do not do the heavy options/variant stuff here const product = await getProduct(params.handle); if (!product) return notFound(); diff --git a/app/search/[collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx similarity index 87% rename from app/search/[collection]/page.tsx rename to app/search/(collection)/[...collection]/page.tsx index 4ec62f2f4..847aaa258 100644 --- a/app/search/[collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -1,4 +1,4 @@ -import { getCollection, getCollectionProducts } from 'lib/shopify'; +import { getCollection, getCollectionProducts } from 'lib/shopware'; import { Metadata } from 'next'; import { notFound } from 'next/navigation'; @@ -33,7 +33,12 @@ export default async function CategoryPage({ }) { const { sort } = searchParams as { [key: string]: string }; const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort; - const products = await getCollectionProducts({ collection: params.collection, sortKey, reverse }); + + const products = await getCollectionProducts({ + collection: params.collection, + sortKey, + reverse + }); return (
diff --git a/app/search/[collection]/opengraph-image.tsx b/app/search/(collection)/opengraph-image.tsx similarity index 88% rename from app/search/[collection]/opengraph-image.tsx rename to app/search/(collection)/opengraph-image.tsx index 9eb9c47f7..98cd40853 100644 --- a/app/search/[collection]/opengraph-image.tsx +++ b/app/search/(collection)/opengraph-image.tsx @@ -1,5 +1,5 @@ import OpengraphImage from 'components/opengraph-image'; -import { getCollection } from 'lib/shopify'; +import { getCollection } from 'lib/shopware'; export const runtime = 'edge'; diff --git a/app/search/page.tsx b/app/search/page.tsx index 354a2022f..3eeac0c6a 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -1,7 +1,7 @@ import Grid from 'components/grid'; import ProductGridItems from 'components/layout/product-grid-items'; import { defaultSort, sorting } from 'lib/constants'; -import { getProducts } from 'lib/shopify'; +import { getSearchCollectionProducts } from 'lib/shopware'; export const runtime = 'edge'; @@ -18,7 +18,7 @@ export default async function SearchPage({ const { sort, q: searchValue } = searchParams as { [key: string]: string }; const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort; - const products = await getProducts({ sortKey, reverse, query: searchValue }); + const products = await getSearchCollectionProducts({ sortKey, reverse, query: searchValue }); const resultsText = products.length > 1 ? 'results' : 'result'; return ( diff --git a/components/carousel.tsx b/components/carousel.tsx index d86d17f45..86f05c54c 100644 --- a/components/carousel.tsx +++ b/components/carousel.tsx @@ -1,39 +1,41 @@ -import { getCollectionProducts } from 'lib/shopify'; +import { getCollectionProducts } from 'lib/shopware'; 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: 'hidden-homepage-carousel' }); - if (!products?.length) return null; + // if (!products?.length) return null; - return ( -
-
- {[...products, ...products].map((product, i) => ( - - {product.featuredImage ? ( - {product.title} - ) : null} -
-
- {product.title} -
-
- - ))} -
-
- ); + return null; + + // return ( + //
+ //
+ // {[...products, ...products].map((product, i) => ( + // + // {product.featuredImage ? ( + // {product.title} + // ) : null} + //
+ //
+ // {product.title} + //
+ //
+ // + // ))} + //
+ //
+ // ); } diff --git a/components/cart/index.tsx b/components/cart/index.tsx index 75b6978fe..b75166bd2 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -3,21 +3,18 @@ import { cookies } from 'next/headers'; import CartModal from './modal'; export default async function Cart() { - const cartId = cookies().get('cartId')?.value; - let cartIdUpdated = false; - let cart; - - if (cartId) { - cart = await getCart(cartId); - } - - // If the `cartId` from the cookie is not set or the cart is empty - // (old carts becomes `null` when you checkout), then get a new `cartId` - // and re-fetch the cart. - if (!cartId || !cart) { - cart = await createCart(); - cartIdUpdated = true; - } - - return ; + // const cartId = cookies().get('cartId')?.value; + // let cartIdUpdated = false; + // let cart; + // if (cartId) { + // cart = await getCart(cartId); + // } + // // If the `cartId` from the cookie is not set or the cart is empty + // // (old carts becomes `null` when you checkout), then get a new `cartId` + // // and re-fetch the cart. + // if (!cartId || !cart) { + // cart = await createCart(); + // cartIdUpdated = true; + // } + // return ; } diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index 2280de26e..a47af2ed5 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -1,6 +1,6 @@ import { GridTileImage } from 'components/grid/tile'; -import { getCollectionProducts } from 'lib/shopify'; -import type { Product } from 'lib/shopify/types'; +import { getCollectionProducts } from 'lib/shopware'; +import type { Product } from 'lib/shopware/types'; import Link from 'next/link'; function ThreeItemGridItem({ @@ -16,7 +16,7 @@ function ThreeItemGridItem({
- +
diff --git a/components/layout/navbar/index.tsx b/components/layout/navbar/index.tsx index 8d036b315..301982468 100644 --- a/components/layout/navbar/index.tsx +++ b/components/layout/navbar/index.tsx @@ -4,20 +4,20 @@ import { Suspense } from 'react'; import Cart from 'components/cart'; import CartIcon from 'components/icons/cart'; import LogoIcon from 'components/icons/logo'; -import { getMenu } from 'lib/shopify'; -import { Menu } from 'lib/shopify/types'; +import { getMenu } from 'lib/shopware'; +import { Menu } from 'lib/shopware/types'; import MobileMenu from './mobile-menu'; import Search from './search'; export default async function Navbar() { - const menu = await getMenu('next-js-frontend-header-menu'); + const menu = await getMenu({ type: 'main-navigation' }); return ( ))} diff --git a/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts b/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts index b223d1509..2bc67f1e3 100644 --- a/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts +++ b/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts @@ -950,7 +950,7 @@ export type components = { position: number; sectionId: string; sectionPosition?: string; - slots?: components['schemas']['CmsSlot']; + slots?: components['schemas']['CmsSlot'][]; type: string; /** Format: date-time */ updatedAt?: string; @@ -993,7 +993,7 @@ export type components = { name?: string; previewMedia?: components['schemas']['Media']; previewMediaId?: string; - sections?: components['schemas']['CmsSection']; + sections?: components['schemas']['CmsSection'][]; translated?: { cssClass?: string; entity?: string; @@ -1029,7 +1029,7 @@ export type components = { backgroundMedia?: components['schemas']['Media']; backgroundMediaId?: string; backgroundMediaMode?: string; - blocks?: components['schemas']['CmsBlock']; + blocks?: components['schemas']['CmsBlock'][]; cmsPageVersionId?: string; /** Format: date-time */ createdAt: string; diff --git a/lib/shopware/criteria.ts b/lib/shopware/criteria.ts index 52b41e929..8b323186f 100644 --- a/lib/shopware/criteria.ts +++ b/lib/shopware/criteria.ts @@ -85,6 +85,28 @@ export function getDefaultCategoryCriteria(page: number = 1, limit: number = 1) }; } +export function getDefaultCategoryWithCmsCriteria(page: number = 1, limit: number = 1) { + return { + page: page, + limit: limit, + associations: { + cmsPage: { + associations: { + sections: { + associations: { + blocks: { + associations: { + slots: {} + } + } + } + } + } + } + } + }; +} + export function getStaticCollectionCriteria(page: number = 1, limit: number = 20) { return { page: page, diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 4b54e6806..54e90652f 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -8,6 +8,7 @@ import { } from './types'; import { getDefaultCategoryCriteria, + getDefaultCategoryWithCmsCriteria, getDefaultCrossSellingCriteria, getDefaultProductCriteria, getDefaultProductsCriteria, @@ -26,29 +27,31 @@ import { requestSeoUrl } from './api'; import { + transformCollection, + transformHandle, transformMenu, transformPage, transformProduct, transformProducts, - transformCollection, transformStaticCollection } from './transform'; -export async function getMenu(params?: { type?: StoreNavigationTypeSW, depth?: number }): Promise { +export async function getMenu(params?: { + type?: StoreNavigationTypeSW; + depth?: number; +}): Promise { const type = params?.type || 'main-navigation'; const depth = params?.depth || 1; const res = await requestNavigation(type, depth); - return res ? transformMenu(res) : []; + return res ? transformMenu(res, type) : []; } -export async function getPage(handle: string): Promise { - const seoUrlElement = await getFirstSeoUrlElement(handle); +export async function getPage(handle: string | []): Promise { + const pageHandle = transformHandle(handle).replace('cms/', ''); + const seoUrlElement = await getFirstSeoUrlElement(pageHandle); if (seoUrlElement) { - const resCategory = await requestCategory( - seoUrlElement.foreignKey, - getDefaultCategoryCriteria() - ); + const resCategory = await getCategory(seoUrlElement); return resCategory ? transformPage(seoUrlElement, resCategory) : undefined; } @@ -107,15 +110,11 @@ export async function getCollectionProducts(params?: { defaultSearchCriteria?: Partial; }): Promise { let res; - let collectionName: string | [] | undefined = params?.collection; let category = params?.categoryId; + const collectionName = transformHandle(params?.collection ?? ''); const sorting = getSortingCriteria(params?.sortKey, params?.reverse); - if (!category && collectionName) { - // this part is important for the catch all collection handle/slug - if (Array.isArray(collectionName)) { - collectionName = collectionName.join('/'); - } + if (!category && collectionName !== '') { const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { category = seoUrlElement.foreignKey; @@ -139,18 +138,22 @@ export async function getCollectionProducts(params?: { return res ? transformProducts(res) : []; } +export async function getCategory( + seoUrl: ApiSchemas['SeoUrl'], + cms: boolean = false +): Promise { + const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria(); + const resCategory = await requestCategory(seoUrl.foreignKey, criteria); + + return resCategory; +} + // This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx export async function getCollection(handle: string | []) { - let collectionName: string | [] | undefined = handle; - if (Array.isArray(collectionName)) { - collectionName = collectionName.join('/'); - } + const collectionName = transformHandle(handle); const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { - const resCategory = await requestCategory( - seoUrlElement.foreignKey, - getDefaultCategoryCriteria() - ); + const resCategory = await getCategory(seoUrlElement); const path = seoUrlElement.seoPathInfo ?? ''; const collection = transformCollection(seoUrlElement, resCategory); @@ -164,10 +167,7 @@ export async function getCollection(handle: string | []) { export async function getProduct(handle: string | []): Promise { let productSW: ApiSchemas['Product'] | undefined; let productId: string | undefined; - let productHandle: string | [] | undefined = handle; - if (Array.isArray(productHandle)) { - productHandle = productHandle.join('/'); - } + const productHandle = transformHandle(handle); const seoUrlElement = await getFirstSeoUrlElement(productHandle); if (seoUrlElement) { diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index 97f9202a5..9bff11aad 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -3,28 +3,32 @@ import { CategoryListingResultSW, Collection, Menu, + Page, Product, ProductOption, ProductVariant } from './types'; import { ListItem } from 'components/layout/search/filter'; -export function transformMenu(res: ApiSchemas['NavigationRouteResponse']) { +export function transformMenu(res: ApiSchemas['NavigationRouteResponse'], type: string) { let menu: Menu[] = []; - res.map((item) => menu.push(transformMenuItem(item))); + res.map((item) => menu.push(transformMenuItem(item, type))); return menu; } -function transformMenuItem(item: ApiSchemas['Category']): Menu { +function transformMenuItem(item: ApiSchemas['Category'], type: string): Menu { + // @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item) return { id: item.id ?? '', title: item.name, - children: item.children?.map((item) => transformMenuItem(item)) ?? [], + children: item.children?.map((item) => transformMenuItem(item, type)) ?? [], path: item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo - ? '/search/' + 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' }; @@ -32,25 +36,52 @@ function transformMenuItem(item: ApiSchemas['Category']): Menu { export function transformPage( seoUrlElement: ApiSchemas['SeoUrl'], - resCategory: ApiSchemas['Category'] -) { + category: ApiSchemas['Category'] +): Page { + let plainHtmlContent; + if (category.cmsPage) { + const cmsPage: ApiSchemas['CmsPage'] = category.cmsPage; + plainHtmlContent = transformToPlainHtmlContent(cmsPage); + } + return { id: seoUrlElement.id ?? '', - title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', + title: category.translated?.metaTitle ?? category.name ?? '', handle: seoUrlElement.seoPathInfo, - body: resCategory.description ?? '', - bodySummary: resCategory.translated?.metaDescription ?? resCategory.description ?? '', + body: plainHtmlContent ?? category.description ?? '', + bodySummary: category.translated?.metaDescription ?? category.description ?? '', seo: { - title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', - description: resCategory.translated?.metaDescription ?? resCategory.description ?? '' + title: category.translated?.metaTitle ?? category.name ?? '', + description: category.translated?.metaDescription ?? category.description ?? '' }, createdAt: seoUrlElement.createdAt ?? '', updatedAt: seoUrlElement.updatedAt ?? '', routeName: seoUrlElement.routeName, + originalCmsPage: category.cmsPage, foreignKey: seoUrlElement.foreignKey }; } +export function transformToPlainHtmlContent(cmsPage: ApiSchemas['CmsPage']): string { + let plainHtmlContent = ''; + + cmsPage.sections?.map((section) => { + section.blocks?.map((block) => { + block.slots?.map((slot) => { + if (slot.slot === 'content' && slot.config?.content) { + const currentContent: string = slot.config.content.value + ''; + // we do not add content with h1, because will be added via template already + if (!currentContent.match(/(<\/?h)([1])/)) { + plainHtmlContent += currentContent; + } + } + }); + }); + }); + + return plainHtmlContent; +} + export function transformCollection( seoUrlElement: ApiSchemas['SeoUrl'], resCategory: ApiSchemas['Category'] @@ -234,3 +265,12 @@ function transformVariants(parent: ApiSchemas['Product']): ProductVariant[] { return productVariants; } + +export function transformHandle(handle: string | []): string { + let collectionName: string | [] | undefined = handle; + if (Array.isArray(collectionName)) { + collectionName = collectionName.join('/'); + } + + return collectionName ?? ''; +} diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts index 12be345b1..a561ba948 100644 --- a/lib/shopware/types.ts +++ b/lib/shopware/types.ts @@ -18,7 +18,7 @@ export type SeoURLResultSW = { } & ApiSchemas['EntitySearchResult']; /** Vercel Commerce Types */ -export type Menu = { id: string; title: string; path: string, type: string, children: Menu[] }; +export type Menu = { id: string; title: string; path: string; type: string; children: Menu[] }; export type Page = { id: string; @@ -31,6 +31,7 @@ export type Page = { updatedAt: string; routeName?: string; foreignKey?: string; + originalCmsPage?: ApiSchemas['CmsPage']; }; export type ProductOption = { From ed79b5b025df66a5ee84e2351f77e2a073cd3cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 10 Jul 2023 09:17:46 +0200 Subject: [PATCH 05/57] feat(poc): update packages --- package.json | 12 ++-- pnpm-lock.yaml | 162 ++++++++++++++++++++++++------------------------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index b965d5317..94b4051e3 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ }, "dependencies": { "@headlessui/react": "^1.7.15", - "@shopware/api-client": "^0.0.3", + "@shopware/api-client": "0.0.0-canary-20230706101754", "@vercel/og": "^0.5.8", "clsx": "^1.2.1", "is-empty-iterable": "^3.0.0", - "next": "13.4.8", + "next": "13.4.9", "react": "18.2.0", "react-cookie": "^4.1.1", "react-dom": "18.2.0" @@ -35,18 +35,18 @@ "devDependencies": { "@playwright/test": "^1.35.1", "@tailwindcss/typography": "^0.5.9", - "@types/node": "20.4.0", + "@types/node": "^20.4.1", "@types/react": "18.2.14", "@types/react-dom": "18.2.6", "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.14", "eslint": "^8.44.0", - "eslint-config-next": "^13.4.8", + "eslint-config-next": "^13.4.9", "eslint-config-prettier": "^8.8.0", "eslint-plugin-unicorn": "^47.0.0", "lint-staged": "^13.2.3", - "postcss": "^8.4.24", - "prettier": "^2.8.8", + "postcss": "^8.4.25", + "prettier": "^3.0", "prettier-plugin-tailwindcss": "^0.3.0", "tailwindcss": "^3.3.2", "typescript": "5.1.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90958f824..80b4929f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,8 +5,8 @@ dependencies: specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@shopware/api-client': - specifier: ^0.0.3 - version: 0.0.3 + specifier: 0.0.0-canary-20230706101754 + version: 0.0.0-canary-20230706101754 '@vercel/og': specifier: ^0.5.8 version: 0.5.8 @@ -17,8 +17,8 @@ dependencies: specifier: ^3.0.0 version: 3.0.0 next: - specifier: 13.4.8 - version: 13.4.8(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.9 + version: 13.4.9(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -37,8 +37,8 @@ devDependencies: specifier: ^0.5.9 version: 0.5.9(tailwindcss@3.3.2) '@types/node': - specifier: 20.4.0 - version: 20.4.0 + specifier: ^20.4.1 + version: 20.4.1 '@types/react': specifier: 18.2.14 version: 18.2.14 @@ -50,13 +50,13 @@ devDependencies: version: 1.0.0 autoprefixer: specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.24) + version: 10.4.14(postcss@8.4.25) eslint: specifier: ^8.44.0 version: 8.44.0 eslint-config-next: - specifier: ^13.4.8 - version: 13.4.8(eslint@8.44.0)(typescript@5.1.6) + specifier: ^13.4.9 + version: 13.4.9(eslint@8.44.0)(typescript@5.1.6) eslint-config-prettier: specifier: ^8.8.0 version: 8.8.0(eslint@8.44.0) @@ -67,14 +67,14 @@ devDependencies: specifier: ^13.2.3 version: 13.2.3 postcss: - specifier: ^8.4.24 - version: 8.4.24 + specifier: ^8.4.25 + version: 8.4.25 prettier: - specifier: ^2.8.8 - version: 2.8.8 + specifier: ^3.0 + version: 3.0.0 prettier-plugin-tailwindcss: specifier: ^0.3.0 - version: 0.3.0(prettier@2.8.8) + version: 0.3.0(prettier@3.0.0) tailwindcss: specifier: ^3.3.2 version: 3.3.2 @@ -225,18 +225,18 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@next/env@13.4.8: - resolution: {integrity: sha512-twuSf1klb3k9wXI7IZhbZGtFCWvGD4wXTY2rmvzIgVhXhs7ISThrbNyutBx3jWIL8Y/Hk9+woytFz5QsgtcRKQ==} + /@next/env@13.4.9: + resolution: {integrity: sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw==} dev: false - /@next/eslint-plugin-next@13.4.8: - resolution: {integrity: sha512-cmfVHpxWjjcETFt2WHnoFU6EmY69QcPJRlRNAooQlNe53Ke90vg1Ci/dkPffryJZaxxiRziP9bQrV8lDVCn3Fw==} + /@next/eslint-plugin-next@13.4.9: + resolution: {integrity: sha512-nDtGpa992tNyAkT/KmSMy7QkHfNZmGCBYhHtafU97DubqxzNdvLsqRtliQ4FU04CysRCtvP2hg8rRC1sAKUTUA==} dependencies: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.4.8: - resolution: {integrity: sha512-MSFplVM4dTWOuKAUv0XR9gY7AWtMSBu9os9f+kp+s5rWhM1I2CdR3obFttd6366nS/W/VZxbPM5oEIdlIa46zA==} + /@next/swc-darwin-arm64@13.4.9: + resolution: {integrity: sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -244,8 +244,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.4.8: - resolution: {integrity: sha512-Reox+UXgonon9P0WNDE6w85DGtyBqGitl/ryznOvn6TvfxEaZIpTgeu3ZrJLU9dHSMhiK7YAM793mE/Zii2/Qw==} + /@next/swc-darwin-x64@13.4.9: + resolution: {integrity: sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -253,8 +253,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.4.8: - resolution: {integrity: sha512-kdyzYvAYtqQVgzIKNN7e1rLU8aZv86FDSRqPlOkKZlvqudvTO0iohuTPmnEEDlECeBM6qRPShNffotDcU/R2KA==} + /@next/swc-linux-arm64-gnu@13.4.9: + resolution: {integrity: sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -262,8 +262,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.4.8: - resolution: {integrity: sha512-oWxx4yRkUGcR81XwbI+T0zhZ3bDF6V1aVLpG+C7hSG50ULpV8gC39UxVO22/bv93ZlcfMY4zl8xkz9Klct6dpQ==} + /@next/swc-linux-arm64-musl@13.4.9: + resolution: {integrity: sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -271,8 +271,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.4.8: - resolution: {integrity: sha512-anhtvuO6eE9YRhYnaEGTfbpH3L5gT/9qPFcNoi6xS432r/4DAtpJY8kNktqkTVevVIC/pVumqO8tV59PR3zbNg==} + /@next/swc-linux-x64-gnu@13.4.9: + resolution: {integrity: sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -280,8 +280,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.4.8: - resolution: {integrity: sha512-aR+J4wWfNgH1DwCCBNjan7Iumx0lLtn+2/rEYuhIrYLY4vnxqSVGz9u3fXcgUwo6Q9LT8NFkaqK1vPprdq+BXg==} + /@next/swc-linux-x64-musl@13.4.9: + resolution: {integrity: sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -289,8 +289,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.4.8: - resolution: {integrity: sha512-OWBKIrJwQBTqrat0xhxEB/jcsjJR3+diD9nc/Y8F1mRdQzsn4bPsomgJyuqPVZs6Lz3K18qdIkvywmfSq75SsQ==} + /@next/swc-win32-arm64-msvc@13.4.9: + resolution: {integrity: sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -298,8 +298,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.4.8: - resolution: {integrity: sha512-agiPWGjUndXGTOn4ChbKipQXRA6/UPkywAWIkx7BhgGv48TiJfHTK6MGfBoL9tS6B4mtW39++uy0wFPnfD0JWg==} + /@next/swc-win32-ia32-msvc@13.4.9: + resolution: {integrity: sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -307,8 +307,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.4.8: - resolution: {integrity: sha512-UIRKoByVKbuR6SnFG4JM8EMFlJrfEGuUQ1ihxzEleWcNwRMMiVaCj1KyqfTOW8VTQhJ0u8P1Ngg6q1RwnIBTtw==} + /@next/swc-win32-x64-msvc@13.4.9: + resolution: {integrity: sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -354,7 +354,7 @@ packages: engines: {node: '>=16'} hasBin: true dependencies: - '@types/node': 20.4.0 + '@types/node': 20.4.1 playwright-core: 1.35.1 optionalDependencies: fsevents: 2.3.2 @@ -369,8 +369,8 @@ packages: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: true - /@shopware/api-client@0.0.3: - resolution: {integrity: sha512-lz8xxDAZ32JjILXMfhmv5xOc+yUMBTw/+Br1iwnEUbXUY0QCQ12coNA0P0vA675wXL09JIfSLg27A61cOud70Q==} + /@shopware/api-client@0.0.0-canary-20230706101754: + resolution: {integrity: sha512-h7nCTWVu6bLbxdKT8vEJcUVKVu/RveaZ3M1PxrQAMiP6b2nQeqRh5+QlqTEFGk+Middy11MINH70Wp6GMx+Y+A==} dependencies: ofetch: 1.1.1 dev: false @@ -417,8 +417,8 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.4.0: - resolution: {integrity: sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==} + /@types/node@20.4.1: + resolution: {integrity: sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==} dev: true /@types/normalize-package-data@2.4.1: @@ -675,7 +675,7 @@ packages: engines: {node: '>=8'} dev: true - /autoprefixer@10.4.14(postcss@8.4.24): + /autoprefixer@10.4.14(postcss@8.4.25): resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -687,7 +687,7 @@ packages: fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.24 + postcss: 8.4.25 postcss-value-parser: 4.2.0 dev: true @@ -1186,8 +1186,8 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-next@13.4.8(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-2hE0b6lHuhtHBX8VgEXi8v4G8PVrPUBMOSLCTq8qtcQ2qQOX7+uBOLK2kU4FD2qDZzyXNlhmuH+WLT5ptY4XLA==} + /eslint-config-next@13.4.9(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-0fLtKRR268NArpqeXXwnLgMXPvF64YESQvptVg+RMLCaijKm3FICN9Y7Jc1p2o+yrWwE4DufJXDM/Vo53D1L7g==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1195,7 +1195,7 @@ packages: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 13.4.8 + '@next/eslint-plugin-next': 13.4.9 '@rushstack/eslint-patch': 1.3.2 '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.6) eslint: 8.44.0 @@ -1204,7 +1204,7 @@ packages: eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.44.0) eslint-plugin-react: 7.32.2(eslint@8.44.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.44.0) + eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0) typescript: 5.1.6 transitivePeerDependencies: - eslint-import-resolver-webpack @@ -1342,8 +1342,8 @@ packages: semver: 6.3.0 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.44.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + /eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0): + resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 @@ -2324,8 +2324,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next@13.4.8(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-lxUjndYKjZHGK3CWeN2RI+/6ni6EUvjiqGWXAYPxUfGIdFGQ5XoisrqAJ/dF74aP27buAfs8MKIbIMMdxjqSBg==} + /next@13.4.9(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: @@ -2342,7 +2342,7 @@ packages: sass: optional: true dependencies: - '@next/env': 13.4.8 + '@next/env': 13.4.9 '@swc/helpers': 0.5.1 busboy: 1.6.0 caniuse-lite: 1.0.30001512 @@ -2353,15 +2353,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.8 - '@next/swc-darwin-x64': 13.4.8 - '@next/swc-linux-arm64-gnu': 13.4.8 - '@next/swc-linux-arm64-musl': 13.4.8 - '@next/swc-linux-x64-gnu': 13.4.8 - '@next/swc-linux-x64-musl': 13.4.8 - '@next/swc-win32-arm64-msvc': 13.4.8 - '@next/swc-win32-ia32-msvc': 13.4.8 - '@next/swc-win32-x64-msvc': 13.4.8 + '@next/swc-darwin-arm64': 13.4.9 + '@next/swc-darwin-x64': 13.4.9 + '@next/swc-linux-arm64-gnu': 13.4.9 + '@next/swc-linux-arm64-musl': 13.4.9 + '@next/swc-linux-x64-gnu': 13.4.9 + '@next/swc-linux-x64-musl': 13.4.9 + '@next/swc-win32-arm64-msvc': 13.4.9 + '@next/swc-win32-ia32-msvc': 13.4.9 + '@next/swc-win32-x64-msvc': 13.4.9 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -2653,29 +2653,29 @@ packages: engines: {node: '>=4'} dev: true - /postcss-import@15.1.0(postcss@8.4.24): + /postcss-import@15.1.0(postcss@8.4.25): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.25 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.2 dev: true - /postcss-js@4.0.1(postcss@8.4.24): + /postcss-js@4.0.1(postcss@8.4.25): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.24 + postcss: 8.4.25 dev: true - /postcss-load-config@4.0.1(postcss@8.4.24): + /postcss-load-config@4.0.1(postcss@8.4.25): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -2688,17 +2688,17 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.24 + postcss: 8.4.25 yaml: 2.3.1 dev: true - /postcss-nested@6.0.1(postcss@8.4.24): + /postcss-nested@6.0.1(postcss@8.4.25): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.24 + postcss: 8.4.25 postcss-selector-parser: 6.0.13 dev: true @@ -2730,8 +2730,8 @@ packages: source-map-js: 1.0.2 dev: false - /postcss@8.4.24: - resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + /postcss@8.4.25: + resolution: {integrity: sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -2744,7 +2744,7 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.3.0(prettier@2.8.8): + /prettier-plugin-tailwindcss@0.3.0(prettier@3.0.0): resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} engines: {node: '>=12.17.0'} peerDependencies: @@ -2796,12 +2796,12 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - prettier: 2.8.8 + prettier: 3.0.0 dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} + /prettier@3.0.0: + resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + engines: {node: '>=14'} hasBin: true dev: true @@ -3308,11 +3308,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.24 - postcss-import: 15.1.0(postcss@8.4.24) - postcss-js: 4.0.1(postcss@8.4.24) - postcss-load-config: 4.0.1(postcss@8.4.24) - postcss-nested: 6.0.1(postcss@8.4.24) + postcss: 8.4.25 + postcss-import: 15.1.0(postcss@8.4.25) + postcss-js: 4.0.1(postcss@8.4.25) + postcss-load-config: 4.0.1(postcss@8.4.25) + postcss-nested: 6.0.1(postcss@8.4.25) postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 resolve: 1.22.2 From fc39573c57eddd97d4daf2815448816b01e99080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 10 Jul 2023 09:57:15 +0200 Subject: [PATCH 06/57] feat(poc): add page parameter for collection --- app/search/(collection)/[...collection]/page.tsx | 3 ++- lib/shopware/criteria.ts | 2 +- lib/shopware/index.ts | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index 847aaa258..1f8d4dfbf 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -31,11 +31,12 @@ export default async function CategoryPage({ params: { collection: string }; searchParams?: { [key: string]: string | string[] | undefined }; }) { - const { sort } = searchParams as { [key: string]: string }; + const { sort, page } = searchParams as { [key: string]: string }; const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort; const products = await getCollectionProducts({ collection: params.collection, + page: page ? parseInt(page) : 1, sortKey, reverse }); diff --git a/lib/shopware/criteria.ts b/lib/shopware/criteria.ts index 8b323186f..9abaa0899 100644 --- a/lib/shopware/criteria.ts +++ b/lib/shopware/criteria.ts @@ -1,6 +1,6 @@ export function getDefaultProductsCriteria(page: number = 1, limit: number = 15) { return { - page: page, + p: page, limit: limit, associations: getDefaultProductAssociations(), filter: [ diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 54e90652f..4901c21cf 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -104,6 +104,7 @@ export async function getSearchCollectionProducts(params?: { export async function getCollectionProducts(params?: { collection: string; + page?: number; reverse?: boolean; sortKey?: string; categoryId?: string; @@ -129,7 +130,7 @@ export async function getCollectionProducts(params?: { if (category) { const criteria = !params?.defaultSearchCriteria - ? getDefaultProductsCriteria() + ? getDefaultProductsCriteria(params?.page) : params?.defaultSearchCriteria; const productsCriteria = { ...criteria, ...sorting }; res = await requestCategoryProductsCollection(category, productsCriteria); From ac958f860fe454a7c5f854711c85ce708015789d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 10 Jul 2023 11:21:15 +0200 Subject: [PATCH 07/57] feat(poc): revert prettier update --- lib/shopware/api.ts | 19 +++++++++++++++++-- lib/shopware/types.ts | 8 ++++++-- package.json | 2 +- pnpm-lock.yaml | 16 ++++++++-------- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 82855dd35..39fb9a479 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,5 +1,6 @@ -import { createAPIClient } from '@shopware/api-client'; -import { operations, operationPaths } from './api-types/apiTypes-6.5.2.0'; +import { createAPIClient, RequestReturnType, RequestParameters } from '@shopware/api-client'; +//import { operations, operationPaths } from './api-types/apiTypes-6.5.2.0'; +import { operations, operationPaths } from '@shopware/api-client/api-types'; import { ApiSchemas, CategoryListingResultSW, @@ -17,6 +18,20 @@ const apiInstance = createAPIClient({ apiType: 'store-api' }); +export type ExtendedOperations = {}; + +// reimport operations request parameters to use it in application +export type ApiRequestParams = RequestParameters< + ExtendedOperations, + operations +>; + +// reimport operations return types to use it in application +export type ApiReturnType = RequestReturnType< + OPERATION_NAME, + operations +>; + export async function requestNavigation( type: StoreNavigationTypeSW, depth: number diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts index a561ba948..8f0a1ae7a 100644 --- a/lib/shopware/types.ts +++ b/lib/shopware/types.ts @@ -1,4 +1,4 @@ -import { components } from './api-types/apiTypes-6.5.2.0'; +import { components } from '@shopware/api-client/api-types'; /** Shopware Types */ @@ -7,7 +7,11 @@ export type ApiSchemas = components['schemas']; export type StoreNavigationTypeSW = 'main-navigation' | 'footer-navigation' | 'service-navigation'; export type ProductListingCriteria = ApiSchemas['ProductListingCriteria'] & { query: string; -}; +} & Omit & { Criteria: { filter?: { + field: string; + type: string; + value: string | null | boolean; +}[]}}; /** Return Types */ export type CategoryListingResultSW = { diff --git a/package.json b/package.json index 94b4051e3..f749df541 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint-plugin-unicorn": "^47.0.0", "lint-staged": "^13.2.3", "postcss": "^8.4.25", - "prettier": "^3.0", + "prettier": "^2.8.8", "prettier-plugin-tailwindcss": "^0.3.0", "tailwindcss": "^3.3.2", "typescript": "5.1.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80b4929f7..1be262caa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,11 +70,11 @@ devDependencies: specifier: ^8.4.25 version: 8.4.25 prettier: - specifier: ^3.0 - version: 3.0.0 + specifier: ^2.8.8 + version: 2.8.8 prettier-plugin-tailwindcss: specifier: ^0.3.0 - version: 0.3.0(prettier@3.0.0) + version: 0.3.0(prettier@2.8.8) tailwindcss: specifier: ^3.3.2 version: 3.3.2 @@ -2744,7 +2744,7 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.3.0(prettier@3.0.0): + /prettier-plugin-tailwindcss@0.3.0(prettier@2.8.8): resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} engines: {node: '>=12.17.0'} peerDependencies: @@ -2796,12 +2796,12 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - prettier: 3.0.0 + prettier: 2.8.8 dev: true - /prettier@3.0.0: - resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} - engines: {node: '>=14'} + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} hasBin: true dev: true From 9c89c3600894ee1c9965280532b650e51a874511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 10 Jul 2023 13:50:49 +0200 Subject: [PATCH 08/57] feat(poc): remove own apiTypes file, extend api --- lib/shopware/api-extended.ts | 142 + lib/shopware/api-types/apiTypes-6.5.2.0.d.ts | 7733 ------------------ lib/shopware/api.ts | 16 +- lib/shopware/types.ts | 10 +- 4 files changed, 150 insertions(+), 7751 deletions(-) create mode 100644 lib/shopware/api-extended.ts delete mode 100644 lib/shopware/api-types/apiTypes-6.5.2.0.d.ts diff --git a/lib/shopware/api-extended.ts b/lib/shopware/api-extended.ts new file mode 100644 index 000000000..b912ced0c --- /dev/null +++ b/lib/shopware/api-extended.ts @@ -0,0 +1,142 @@ +import { operations, operationPaths, components } from '@shopware/api-client/api-types'; + +type operationsWithoutOriginal = Omit< + operations, + 'readProduct' | 'searchPage' | 'readProductListing' +>; +export type extendedPaths = 'readProduct post /product' | operationPaths; +export type extendedOperations = operationsWithoutOriginal & { + readProduct: extendedReadProduct; + searchPage: extendedSearchPage; + readProductListing: extendedReadProductListing; +}; + +export type ExtendedCriteria = Omit & { + filter?: { + field: string; + type: string; + value: string | boolean | null; + }[]; +}; + +type extendedReadProduct = { + requestBody?: { + content: { + 'application/json': ExtendedCriteria; + }; + }; + responses: { + /** Entity search result containing products */ + 200: { + content: { + 'application/json': { + elements?: components['schemas']['Product'][]; + } & components['schemas']['EntitySearchResult']; + }; + }; + }; +}; + +type ExtendedProductCriteria = ExtendedCriteria & { + /** Number of items per result page. If not set, the limit will be set according to the default products per page, defined in the system settings. */ + limit?: number; + /** Filter by manufacturers. List of manufacturer identifiers separated by a `|`. */ + manufacturer?: string; + /** + * Enables/disabled filtering by manufacturer. If set to false, the `manufacturer` filter will be ignored. Also the `aggregations[manufacturer]` key will be removed from the response. + * @default true + */ + 'manufacturer-filter'?: boolean; + /** + * Filters by a maximum product price. Has to be higher than the `min-price` filter. + * @default 0 + */ + 'max-price'?: number; + /** + * Filters by a minimum product price. Has to be lower than the `max-price` filter. + * @default 0 + */ + 'min-price'?: number; + /** Specifies the sorting of the products by `availableSortings`. If not set, the default sorting will be set according to the shop settings. The available sorting options are sent within the response under the `availableSortings` key. In order to sort by a field, consider using the `sort` parameter from the listing criteria. Do not use both parameters together, as it might lead to unexpected results. */ + order?: string; + /** + * Search result page + * @default 1 + */ + p?: number; + /** + * Enables/disabled filtering by price. If set to false, the `min-price` and `max-price` filter will be ignored. Also the `aggregations[price]` key will be removed from the response. + * @default true + */ + 'price-filter'?: boolean; + /** Filters products by their properties. List of property identifiers separated by a `|`. */ + properties?: string; + /** + * Enables/disabled filtering by properties products. If set to false, the `properties` filter will be ignored. Also the `aggregations[properties]` key will be removed from the response. + * @default true + */ + 'property-filter'?: boolean; + /** A whitelist of property identifiers which can be used for filtering. List of property identifiers separated by a `|`. The `property-filter` must be `true`, otherwise the whitelist has no effect. */ + 'property-whitelist'?: string; + /** Filter products with a minimum average rating. */ + rating?: number; + /** + * Enables/disabled filtering by rating. If set to false, the `rating` filter will be ignored. Also the `aggregations[rating]` key will be removed from the response. + * @default true + */ + 'rating-filter'?: boolean; + /** By sending the parameter `reduce-aggregations` , the post-filters that were applied by the customer, are also applied to the aggregations. This has the consequence that only values are returned in the aggregations that would lead to further filter results. This parameter is a flag, the value has no effect. */ + 'reduce-aggregations'?: string | null; + /** + * Filters products that are marked as shipping-free. + * @default false + */ + 'shipping-free'?: boolean; + /** + * Enables/disabled filtering by shipping-free products. If set to false, the `shipping-free` filter will be ignored. Also the `aggregations[shipping-free]` key will be removed from the response. + * @default true + */ + 'shipping-free-filter'?: boolean; +}; + +type extendedSearchPage = { + requestBody?: { + content: { + 'application/json': { + /** Using the search parameter, the server performs a text search on all records based on their data model and weighting as defined in the entity definition using the SearchRanking flag. */ + search: string; + } & ExtendedProductCriteria & + components['schemas']['ProductListingFlags']; + }; + }; + responses: { + /** Returns a product listing containing all products and additional fields to display a listing. */ + 200: { + content: { + 'application/json': components['schemas']['ProductListingResult']; + }; + }; + }; +}; + +type extendedReadProductListing = { + parameters: { + path: { + /** Identifier of a category. */ + categoryId: string; + }; + }; + requestBody?: { + content: { + 'application/json': ExtendedProductCriteria & components['schemas']['ProductListingFlags']; + }; + }; + responses: { + /** Returns a product listing containing all products and additional fields to display a listing. */ + 200: { + content: { + 'application/json': components['schemas']['ProductListingResult']; + }; + }; + }; +}; diff --git a/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts b/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts deleted file mode 100644 index 2bc67f1e3..000000000 --- a/lib/shopware/api-types/apiTypes-6.5.2.0.d.ts +++ /dev/null @@ -1,7733 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -/** OneOf type helpers */ -type Without = { [P in Exclude]?: never }; -type XOR = T | U extends object ? (Without & U) | (Without & T) : T | U; -type OneOf = T extends [infer Only] - ? Only - : T extends [infer A, infer B, ...infer Rest] - ? OneOf<[XOR, ...Rest]> - : never; - -type GenericRecord = never | { [key: string]: GenericRecord }; - -export type paths = { - '/account/address': { - /** - * Create a new address for a customer - * Creates a new address for a customer. - */ - post: operations['createCustomerAddress']; - }; - '/account/address/{addressId}': { - /** - * Delete an address of a customer - * Delete an address of customer. - * - * Only addresses which are not set as default addresses for shipping or billing can be deleted. You can check the current default addresses of your customer using the profile information endpoint and change them using the default address endpoint. - * - * **A customer must have at least one address (which can be used for shipping and billing).** - * - * An automatic fallback is not applied. - */ - delete: operations['deleteCustomerAddress']; - /** - * Modify an address of a customer - * Modifies an existing address of a customer. - */ - patch: operations['updateCustomerAddress']; - }; - '/account/address/default-billing/{addressId}': { - /** - * Change a customer's default billing address - * Updates the default (preselected) billing addresses of a customer. - */ - patch: operations['defaultBillingAddress']; - }; - '/account/address/default-shipping/{addressId}': { - /** - * Change a customer's default shipping address - * Updates the default (preselected) shipping addresses of a customer. - */ - patch: operations['defaultShippingAddress']; - }; - '/account/change-email': { - /** - * Change the customer's email address - * Changes a customer's email address to a new email address, using their current password as a validation. - */ - post: operations['changeEmail']; - }; - '/account/change-language': { - /** - * Change the customer's language. - * Changes the language of the logged in customer - */ - post: operations['changeLanguage']; - }; - '/account/change-password': { - /** - * Change the customer's password - * Changes a customer's password using their current password as a validation. - */ - post: operations['changePassword']; - }; - '/account/change-payment-method/{paymentMethodId}': { - /** - * Change the customer's default payment method - * Changes a customer's default (preselected) payment method. - */ - post: operations['changePaymentMethod']; - }; - '/account/change-profile': { - /** - * Change the customer's information - * Make changes to a customer's account, like changing their name, salutation or title. - */ - post: operations['changeProfile']; - }; - '/account/customer': { - /** - * Get information about current customer - * Returns information about the current customer. - */ - post: operations['readCustomer']; - /** - * Delete the customer's profile - * Deletes a customer profile along with their addresses, wishlists and associated data. Created orders and their payment/shipping information (addresses) and reviews are not deleted. - */ - delete: operations['deleteCustomer']; - }; - '/account/customer-recovery-is-expired': { - /** - * Checks if the customer recovery entry for a given hash is expired. - * This can be used to validate a provided hash has a valid and not expired customer recovery hash. - */ - post: operations['getCustomerRecoveryIsExpired']; - }; - '/account/list-address': { - /** - * Fetch addresses of a customer - * Lists all addresses of the current customer and allows filtering them based on a criteria. - */ - post: operations['listAddress']; - }; - '/account/login': { - /** - * Log in a customer - * Logs in customers given their credentials. - */ - post: operations['loginCustomer']; - }; - '/account/logout': { - /** - * Log out a customer - * Logs out a customer. - */ - post: operations['logoutCustomer']; - }; - '/account/newsletter-recipient': { - /** - * Fetch newsletter recipients - * Perform a filtered search for newsletter recipients. - */ - post: operations['readNewsletterRecipient']; - }; - '/account/recovery-password': { - /** - * Send a password recovery mail - * This operation is Step 1 of the password reset flow. Make sure to implement Step 2 "Reset password with recovery credentials" in order to allow for the complete flow in your application. Sends a recovery mail containing a link with credentials that allows a customer to reset their password. - */ - post: operations['sendRecoveryMail']; - }; - '/account/recovery-password-confirm': { - /** - * Reset a password with recovery credentials - * This operation is Step 2 of the password reset flow. It is required to conduct Step 1 "Send a password recovery mail" in order to obtain the required credentials for this step.Resets a customer's password using credentials from a password recovery mail as a validation. - */ - post: operations['recoveryPassword']; - }; - '/account/register': { - /** - * Register a customer - * Registers a customer. Used both for normal customers and guest customers.See the Guide "Register a customer" for more information on customer registration. - */ - post: operations['register']; - }; - '/account/register-confirm': { - /** - * Confirm a customer registration - * Confirms a customer registration when double opt-in is activated. - * - * Learn more about double opt-in registration in our guide "Register a customer". - */ - post: operations['registerConfirm']; - }; - '/category': { - /** - * Fetch a list of categories - * Perform a filtered search for categories. - */ - post: operations['readCategoryList']; - }; - '/category/{navigationId}': { - /** - * Fetch a single category - * This endpoint returns information about the category, as well as a fully resolved (hydrated with mapping values) CMS page, if one is assigned to the category. You can pass slots which should be resolved exclusively. - */ - post: operations['readCategory']; - }; - '/checkout/cart': { - /** - * Fetch or create a cart - * Used to fetch the current cart or for creating a new one. - */ - get: operations['readCart']; - /** - * Delete a cart - * This route deletes the cart of the customer. - */ - delete: operations['deleteCart']; - }; - '/checkout/cart/line-item': { - /** - * Add items to the cart - * This route adds items to the cart. An item can be a product or promotion for example. They are referenced by the `referencedId`-parameter. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#adding-new-items-to-the-cart) - */ - post: operations['addLineItem']; - /** - * Remove items from the cart - * This route removes items from the cart and recalculates it. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#deleting-items-in-the-cart) - */ - delete: operations['removeLineItem']; - /** - * Update items in the cart - * This route updates items in the cart. A typical example is updating the quantity of an item. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#updating-items-in-the-cart) - */ - patch: operations['updateLineItem']; - }; - '/checkout/order': { - /** - * Create an order from a cart - * Creates a new order from the current cart and deletes the cart. - * - * If you are using the [prepared payment flow](https://developer.shopware.com/docs/concepts/commerce/checkout-concept/payments#2.1-prepare-payment-optional), this endpoint also receives additional transaction details. The exact name of the parameters depends on the implementation of the corresponding *payment handler*. - */ - post: operations['createOrder']; - }; - '/cms/{id}': { - /** - * Fetch and resolve a CMS page - * Loads a content management page by its identifier and resolve the slot data. This could be media files, product listing and so on. - * - * **Important notice** - * - * The criteria passed with this route also affects the listing, if there is one within the cms page. - */ - post: operations['readCms']; - }; - '/contact-form': { - /** - * Submit a contact form message - * Used for submitting contact forms. Be aware that there can be more required fields, depending on the system settings. - */ - post: operations['sendContactMail']; - }; - '/context': { - /** - * Fetch the current context - * Fetches the current context. This includes for example the `customerGroup`, `currency`, `taxRules` and many more. - */ - get: operations['readContext']; - /** - * Modify the current context - * Used for switching the context. A typical example would be changing the language or changing the currency. - */ - patch: operations['updateContext']; - }; - '/country': { - /** - * Fetch countries - * Perform a filtered search for countries - */ - post: operations['readCountry']; - }; - '/country-state/{countryId}': { - /** - * Fetch the states of a country - * Perform a filtered search the states for a country - */ - post: operations['readCountryState']; - }; - '/currency': { - /** - * Fetch currencies - * Perform a filtered search for currencies. - */ - post: operations['readCurrency']; - }; - '/customer-group-registration/config/{customerGroupId}': { - /** Fetch registration settings for customer group */ - get: operations['getCustomerGroupRegistrationInfo']; - }; - '/customer/wishlist': { - /** - * Fetch a wishlist - * Fetch a customer's wishlist. Products on the wishlist can be filtered using a criteria object. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - post: operations['readCustomerWishlist']; - }; - '/customer/wishlist/add/{productId}': { - /** - * Add a product to a wishlist - * Adds a product to a customers wishlist. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - post: operations['addProductOnWishlist']; - }; - '/customer/wishlist/delete/{productId}': { - /** - * Remove a product from a wishlist - * Removes a product from a customer's wishlist. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - delete: operations['deleteProductOnWishlist']; - }; - '/customer/wishlist/merge': { - /** - * Create a wishlist for a customer - * Create a new wishlist for a logged in customer or extend the existing wishlist given a set of products. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * A customer can only have a single wishlist. - * * The wishlist feature has to be activated. - */ - post: operations['mergeProductOnWishlist']; - }; - '/document/download/{documentId}/{deepLinkCode}': { - /** - * Download generated document - * Returns blob file of a generated document to download. - */ - post: operations['download']; - }; - '/handle-payment': { - /** - * Initiate a payment for an order - * This generic endpoint is should be called to initiate a payment flow after an order has been created. The details of the payment flow can differ depending on the payment integration and might require calling additional operations or the setup of webhooks. - * - * The endpoint internally calls the payment handler of the payment method currently set for the order. - */ - post: operations['handlePaymentMethod']; - }; - '/landing-page/{landingPageId}': { - /** - * Fetch a landing page with the resolved CMS page - * Loads a landing page by its identifier and resolves the CMS page. - * - * **Important notice** - * - * The criteria passed with this route also affects the listing, if there is one within the cms page. - */ - post: operations['readLandingPage']; - }; - '/language': { - /** - * Fetch languages - * Perform a filtered search for languages. - */ - post: operations['readLanguages']; - }; - '/navigation/{activeId}/{rootId}': { - /** - * Fetch a navigation menu - * This endpoint returns categories that can be used as a page navigation. You can either return them as a tree or as a flat list. You can also control the depth of the tree. - * - * Instead of passing uuids, you can also use one of the following aliases for the activeId and rootId parameters to get the respective navigations of your sales channel. - * - * * main-navigation - * * service-navigation - * * footer-navigation - */ - post: operations['readNavigation']; - }; - '/newsletter/confirm': { - /** - * Confirm a newsletter registration - * You have to use the hash from the link sent out via email to confirm the user registration. - */ - post: operations['confirmNewsletter']; - }; - '/newsletter/subscribe': { - /** - * Create or remove a newsletter subscription - * This route is used to create/remove/confirm a newsletter subscription. - * - * The `option` property controls what should happen: - * * `direct`: The subscription is directly active and does not need a confirmation. - * * `subscribe`: An email will be send to the provided email addrees containing a link to the /newsletter/confirm route. - * The subscription is only successful, if the /newsletter/confirm route is called with the generated hashes. - * * `unsubscribe`: The email address will be removed from the newsletter subscriptions. - * * `confirmSubscribe`: Confirmes the newsletter subscription for the provided email address. - */ - post: operations['subscribeToNewsletter']; - }; - '/newsletter/unsubscribe': { - /** - * Remove a newsletter subscription - * Removes a newsletter recipient from the mailing lists. - */ - post: operations['unsubscribeToNewsletter']; - }; - '/order': { - /** - * Fetch a list of orders - * List orders of a customer. - */ - post: operations['readOrder']; - }; - '/order/download/{orderId}/{downloadId}': { - /** - * Download a purchased file - * Download a file included in the given order and with the given id. Access must be granted. - */ - get: operations['orderDownloadFile']; - }; - '/order/payment': { - /** - * Update the payment method of an order - * Changes the payment method of a specific order. You can use the /order route to find out if the payment method of an order can be changed - take a look at the `paymentChangeable`- array in the response. - */ - post: operations['orderSetPayment']; - }; - '/order/state/cancel': { - /** - * Cancel an order - * Cancels an order. The order state will be set to 'cancelled'. - */ - post: operations['cancelOrder']; - }; - '/payment-method': { - /** Loads all available payment methods */ - post: operations['readPaymentMethod']; - }; - '/product': { - /** - * Fetch a list of products - * List products that match the given criteria. For performance ressons a limit should always be set. - */ - post: operations['readProduct']; - }; - '/product-export/{accessKey}/{fileName}': { - /** Export product export */ - get: operations['readProductExport']; - }; - '/product-listing/{categoryId}': { - /** - * Fetch a product listing by category - * Fetches a product listing for a specific category. It also provides filters, sortings and property aggregations, analogous to the /search endpoint. - */ - post: operations['readProductListing']; - }; - '/product/{productId}': { - /** - * Fetch a single product - * This route is used to load a single product with the corresponding details. In addition to loading the data, the best variant of the product is determined when a parent id is passed. - */ - post: operations['readProductDetail']; - }; - '/product/{productId}/cross-selling': { - /** - * Fetch cross-selling groups of a product - * This route is used to load the cross sellings for a product. A product has several cross selling definitions in which several products are linked. The route returns the cross sellings together with the linked products - */ - post: operations['readProductCrossSellings']; - }; - '/product/{productId}/find-variant': { - /** - * Search for a matching variant by product options. - * Performs a search for product variants and returns the best matching variant. - */ - post: operations['searchProductVariantIds']; - }; - '/product/{productId}/review': { - /** - * Save a product review - * Saves a review for a product. Reviews have to be activated in the settings. - */ - post: operations['saveProductReview']; - }; - '/product/{productId}/reviews': { - /** - * Fetch product reviews - * Perform a filtered search for product reviews. - */ - post: operations['readProductReviews']; - }; - '/salutation': { - /** - * Fetch salutations - * Perform a filtered search for salutations. - */ - post: operations['readSalutation']; - }; - '/script/{hook}': { - /** Access point for different api logics which are provided by apps over script hooks */ - post: operations['postScriptStoreApiRoute']; - }; - '/search': { - /** - * Search for products - * Performs a search for products which can be used to display a product listing. - */ - post: operations['searchPage']; - }; - '/search-suggest': { - /** - * Search for products (suggest) - * Can be used to implement search previews or suggestion listings, that don’t require any interaction. - */ - post: operations['searchSuggest']; - }; - '/seo-url': { - /** - * Fetch SEO routes - * Perform a filtered search for seo urls. - */ - post: operations['readSeoUrl']; - }; - '/shipping-method': { - /** - * Fetch shipping methods - * Perform a filtered search for shipping methods. - */ - post: operations['readShippingMethod']; - }; - '/sitemap': { - /** - * Fetch sitemaps - * Fetches a list of compressed sitemap files, which are often used by search engines. - */ - get: operations['readSitemap']; - }; -}; - -export type webhooks = Record; - -export type components = { - schemas: { - AccountNewsletterRecipientResult: components['schemas']['Struct'] & { - status?: string; - }; - /** Added since version: 6.0.0.0 */ - AclRole: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.1.0 */ - App: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.1.0 */ - AppActionButton: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.15.0 */ - AppAdministrationSnippet: { - appId: string; - /** Format: date-time */ - createdAt: string; - id?: string; - localeId: string; - /** Format: date-time */ - updatedAt?: string; - value: string; - }; - /** Added since version: 6.4.2.0 */ - AppCmsBlock: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.10.0 */ - AppFlowAction: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.5.2.0 */ - AppFlowEvent: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.1.0 */ - AppPaymentMethod: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.10.3 */ - AppScriptCondition: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.1.0 */ - AppTemplate: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - ArrayStruct: components['schemas']['Struct']; - /** Members of the attributes object ("attributes") represent information about the resource object in which it's defined. */ - attributes: GenericRecord; - Cart: components['schemas']['ArrayStruct'] & { - /** An affiliate tracking code */ - affiliateCode?: string; - /** A campaign tracking code */ - campaignCode?: string; - /** A comment that can be added to the cart. */ - customerComment?: string; - /** A list of all cart errors, such as insufficient stocks, invalid addresses or vouchers. */ - errors?: { - key?: string; - level?: string; - message?: string; - }[]; - /** All items within the cart */ - lineItems?: components['schemas']['LineItem'][]; - modified?: boolean; - /** Name of the cart - for example `guest-cart` */ - name?: string; - price?: { - /** - * Format: float - * Net price of the cart - */ - netPrice?: number; - /** - * Format: float - * Price for all line items in the cart - */ - positionPrice?: number; - /** Tax calculation for the cart. One of `gross`, `net` or `tax-free` */ - taxStatus?: string; - /** - * Format: float - * Total price of the cart, including shipping costs, discounts and taxes - */ - totalPrice?: number; - }; - /** Context token identifying the cart and the user session */ - token?: string; - /** A list of all payment transactions associated with the current cart. */ - transactions?: { - paymentMethodId?: string; - }[]; - }; - CartItems: components['schemas']['ArrayStruct'] & { - items?: components['schemas']['LineItem'][]; - }; - /** Added since version: 6.0.0.0 */ - Category: { - active?: boolean; - afterCategoryId?: string; - afterCategoryVersionId?: string; - breadcrumb?: readonly unknown[]; - /** Format: int64 */ - childCount?: number; - children?: components['schemas']['Category'][]; - cmsPage?: components['schemas']['CmsPage']; - cmsPageId?: string; - /** Runtime field, cannot be used as part of the criteria. */ - cmsPageIdSwitched?: boolean; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - customEntityTypeId?: string; - customFields?: GenericRecord; - description?: string; - displayNestedProducts: boolean; - externalLink?: string; - id?: string; - internalLink?: string; - keywords?: string; - /** Format: int64 */ - level?: number; - linkNewTab?: boolean; - linkType?: string; - media?: components['schemas']['Media']; - mediaId?: string; - metaDescription?: string; - metaTitle?: string; - name: string; - parent?: components['schemas']['Category']; - parentId?: string; - parentVersionId?: string; - path?: string; - productAssignmentType: string; - seoUrls?: Array; - translated?: { - afterCategoryId?: string; - afterCategoryVersionId?: string; - cmsPageId?: string; - cmsPageVersionId?: string; - customEntityTypeId?: string; - description?: string; - externalLink?: string; - internalLink?: string; - keywords?: string; - linkType?: string; - mediaId?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - parentId?: string; - parentVersionId?: string; - path?: string; - productAssignmentType?: string; - type?: string; - versionId?: string; - }; - type: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - visible?: boolean; - /** - * Format: int64 - * Runtime field, cannot be used as part of the criteria. - */ - visibleChildCount?: number; - }; - /** Added since version: 6.0.0.0 */ - CategoryJsonApi: components['schemas']['resource'] & { - active?: boolean; - afterCategoryId?: string; - afterCategoryVersionId?: string; - breadcrumb?: readonly unknown[]; - /** Format: int64 */ - childCount?: number; - cmsPageId?: string; - /** Runtime field, cannot be used as part of the criteria. */ - cmsPageIdSwitched?: boolean; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - customEntityTypeId?: string; - customFields?: GenericRecord; - description?: string; - displayNestedProducts: boolean; - externalLink?: string; - id?: string; - internalLink?: string; - keywords?: string; - /** Format: int64 */ - level?: number; - linkNewTab?: boolean; - linkType?: string; - mediaId?: string; - metaDescription?: string; - metaTitle?: string; - name: string; - parentId?: string; - parentVersionId?: string; - path?: string; - productAssignmentType: string; - relationships?: { - children?: { - data?: { - /** @example 0188f8334a2a7224bc6f4f9d2589f76e */ - id?: string; - /** @example category */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /category/0188f8334a2a7224bc6f4f9d23ddd8ec/children - */ - related?: string; - }; - }; - cmsPage?: { - data?: { - /** @example 0188f8334a2a7224bc6f4f9d26fe9251 */ - id?: string; - /** @example cms_page */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /category/0188f8334a2a7224bc6f4f9d23ddd8ec/cmsPage - */ - related?: string; - }; - }; - media?: { - data?: { - /** @example 0188f8334a2a7224bc6f4f9d261b0ef8 */ - id?: string; - /** @example media */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /category/0188f8334a2a7224bc6f4f9d23ddd8ec/media - */ - related?: string; - }; - }; - parent?: { - data?: { - /** @example 0188f8334a2a7224bc6f4f9d24c6c257 */ - id?: string; - /** @example category */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /category/0188f8334a2a7224bc6f4f9d23ddd8ec/parent - */ - related?: string; - }; - }; - seoUrls?: { - data?: { - /** @example 0188f8334a2a7224bc6f4f9d2755e03b */ - id?: string; - /** @example seo_url */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /category/0188f8334a2a7224bc6f4f9d23ddd8ec/seoUrls - */ - related?: string; - }; - }; - }; - translated?: { - afterCategoryId?: string; - afterCategoryVersionId?: string; - cmsPageId?: string; - cmsPageVersionId?: string; - customEntityTypeId?: string; - description?: string; - externalLink?: string; - internalLink?: string; - keywords?: string; - linkType?: string; - mediaId?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - parentId?: string; - parentVersionId?: string; - path?: string; - productAssignmentType?: string; - type?: string; - versionId?: string; - }; - type: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - visible?: boolean; - /** - * Format: int64 - * Runtime field, cannot be used as part of the criteria. - */ - visibleChildCount?: number; - }; - /** Added since version: 6.0.0.0 */ - CmsBlock: { - backgroundColor?: string; - backgroundMedia?: components['schemas']['Media']; - backgroundMediaId?: string; - backgroundMediaMode?: string; - cmsSectionVersionId?: string; - /** Format: date-time */ - createdAt: string; - cssClass?: string; - customFields?: GenericRecord; - extensions?: { - swagCmsExtensionsBlockRule?: { - data?: { - /** @example 0188f8334a2b7161b75c68356973a98a */ - id?: string; - /** @example swag_cms_extensions_block_rule */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /cms-block/0188f8334a2a7224bc6f4f9d284557ed/swagCmsExtensionsBlockRule - */ - related?: string; - }; - }; - swagCmsExtensionsQuickview?: { - data?: { - /** @example 0188f8334a2b7161b75c6835696f14f7 */ - id?: string; - /** @example swag_cms_extensions_quickview */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /cms-block/0188f8334a2a7224bc6f4f9d284557ed/swagCmsExtensionsQuickview - */ - related?: string; - }; - }; - }; - id?: string; - marginBottom?: string; - marginLeft?: string; - marginRight?: string; - marginTop?: string; - name?: string; - /** Format: int64 */ - position: number; - sectionId: string; - sectionPosition?: string; - slots?: components['schemas']['CmsSlot'][]; - type: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - visibility?: { - desktop?: boolean; - mobile?: boolean; - tablet?: boolean; - }; - }; - /** Added since version: 6.0.0.0 */ - CmsPage: { - config?: { - backgroundColor?: string; - }; - /** Format: date-time */ - createdAt: string; - cssClass?: string; - customFields?: GenericRecord; - entity?: string; - extensions?: { - swagCmsExtensionsScrollNavigationPageSettings?: { - data?: { - /** @example 0188f8334a2b7161b75c68356c80b6b2 */ - id?: string; - /** @example swag_cms_extensions_scroll_navigation_page_settings */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /cms-page/0188f8334a2b7161b75c683569f9dfb7/swagCmsExtensionsScrollNavigationPageSettings - */ - related?: string; - }; - }; - }; - id?: string; - landingPages?: components['schemas']['LandingPage']; - name?: string; - previewMedia?: components['schemas']['Media']; - previewMediaId?: string; - sections?: components['schemas']['CmsSection'][]; - translated?: { - cssClass?: string; - entity?: string; - name?: string; - previewMediaId?: string; - type?: string; - versionId?: string; - }; - type: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - CmsPageActivity: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - CmsPageDraft: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CmsSection: { - backgroundColor?: string; - backgroundMedia?: components['schemas']['Media']; - backgroundMediaId?: string; - backgroundMediaMode?: string; - blocks?: components['schemas']['CmsBlock'][]; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - cssClass?: string; - customFields?: GenericRecord; - extensions?: { - swagCmsExtensionsScrollNavigation?: { - data?: { - /** @example 0188f8334a2c70408696d00e502c9862 */ - id?: string; - /** @example swag_cms_extensions_scroll_navigation */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /cms-section/0188f8334a2c70408696d00e4e76e691/swagCmsExtensionsScrollNavigation - */ - related?: string; - }; - }; - }; - id?: string; - mobileBehavior?: string; - name?: string; - page?: components['schemas']['CmsPage']; - pageId: string; - /** Format: int64 */ - position: number; - sizingMode?: string; - type: string; - /** Format: date-time */ - updatedAt?: string; - visibility?: { - desktop?: boolean; - mobile?: boolean; - tablet?: boolean; - }; - }; - /** Added since version: 6.0.0.0 */ - CmsSlot: { - block?: components['schemas']['CmsBlock']; - blockId: string; - cmsBlockVersionId?: string; - config?: GenericRecord; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - data?: GenericRecord; - fieldConfig?: GenericRecord; - id?: string; - locked?: boolean; - slot: string; - translated?: { - blockId?: string; - cmsBlockVersionId?: string; - slot?: string; - type?: string; - versionId?: string; - }; - type: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - ContextTokenResponse: { - /** Context token identifying the current user session. */ - contextToken?: string; - }; - /** Added since version: 6.0.0.0 */ - Country: { - active?: boolean; - addressFormat: GenericRecord; - advancedPostalCodePattern?: string; - checkAdvancedPostalCodePattern?: boolean; - checkPostalCodePattern?: boolean; - checkVatIdPattern?: boolean; - companyTax?: { - /** Format: float */ - amount: number; - currencyId: string; - enabled: boolean; - }; - /** Format: date-time */ - createdAt: string; - customerTax?: { - /** Format: float */ - amount: number; - currencyId: string; - enabled: boolean; - }; - customFields?: GenericRecord; - defaultPostalCodePattern?: string; - displayStateInRegistration?: boolean; - forceStateInRegistration?: boolean; - id?: string; - iso?: string; - iso3?: string; - name: string; - /** Format: int64 */ - position?: number; - postalCodeRequired?: boolean; - shippingAvailable?: boolean; - states?: components['schemas']['CountryState']; - translated?: { - advancedPostalCodePattern?: string; - defaultPostalCodePattern?: string; - iso?: string; - iso3?: string; - name?: string; - vatIdPattern?: string; - }; - /** Format: date-time */ - updatedAt?: string; - vatIdPattern?: string; - vatIdRequired?: boolean; - }; - /** Added since version: 6.0.0.0 */ - CountryJsonApi: components['schemas']['resource'] & { - active?: boolean; - addressFormat: GenericRecord; - advancedPostalCodePattern?: string; - checkAdvancedPostalCodePattern?: boolean; - checkPostalCodePattern?: boolean; - checkVatIdPattern?: boolean; - companyTax?: { - /** Format: float */ - amount: number; - currencyId: string; - enabled: boolean; - }; - /** Format: date-time */ - createdAt: string; - customerTax?: { - /** Format: float */ - amount: number; - currencyId: string; - enabled: boolean; - }; - customFields?: GenericRecord; - defaultPostalCodePattern?: string; - displayStateInRegistration?: boolean; - forceStateInRegistration?: boolean; - id?: string; - iso?: string; - iso3?: string; - name: string; - /** Format: int64 */ - position?: number; - postalCodeRequired?: boolean; - relationships?: { - states?: { - data?: { - /** @example 0188f8334a2d7287b5c9797ef464fd7a */ - id?: string; - /** @example country_state */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /country/0188f8334a2c70408696d00e522c26db/states - */ - related?: string; - }; - }; - }; - shippingAvailable?: boolean; - translated?: { - advancedPostalCodePattern?: string; - defaultPostalCodePattern?: string; - iso?: string; - iso3?: string; - name?: string; - vatIdPattern?: string; - }; - /** Format: date-time */ - updatedAt?: string; - vatIdPattern?: string; - vatIdRequired?: boolean; - }; - /** Added since version: 6.0.0.0 */ - CountryState: { - active?: boolean; - countryId: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - /** Format: int64 */ - position?: number; - shortCode: string; - translated?: { - countryId?: string; - name?: string; - shortCode?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CountryStateJsonApi: components['schemas']['resource'] & { - active?: boolean; - countryId: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - /** Format: int64 */ - position?: number; - shortCode: string; - translated?: { - countryId?: string; - name?: string; - shortCode?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Search parameters. For more information, see our documentation on [Search Queries](https://shopware.stoplight.io/docs/store-api/docs/concepts/search-queries.md#structure) */ - Criteria: { - /** Used to perform aggregations on the search result. For more information, see [Search Queries > Aggregations](https://shopware.stoplight.io/docs/store-api/docs/concepts/search-queries.md#aggregations) */ - aggregations?: { - /** The field you want to aggregate over. */ - field: string; - /** Give your aggregation an identifier, so you can find it easier */ - name: string; - /** The type of aggregation */ - type: string; - }[]; - /** Used to fetch associations which are not fetched by default. */ - associations?: GenericRecord; - /** Fields which should be returned in the search result. */ - fields?: string[]; - /** List of filters to restrict the search result. For more information, see [Search Queries > Filter](https://shopware.stoplight.io/docs/store-api/docs/concepts/search-queries.md#filter) */ - filter?: { - field: string; - type: string; - value: string | null | boolean; - }[]; - /** Perform groupings over certain fields */ - grouping?: string[]; - /** Number of items per result page */ - limit?: number; - /** Search result page */ - page?: number; - /** Filters that applied without affecting aggregations. For more information, see [Search Queries > Post Filter](https://shopware.stoplight.io/docs/store-api/docs/concepts/search-queries.md#post-filter) */ - 'post-filter'?: { - field: string; - type: string; - value: string; - }[]; - /** Sorting in the search result. */ - sort?: { - field: string; - naturalSorting?: boolean; - order?: string; - }[]; - /** - * Whether the total for the total number of hits should be determined for the search query. 0 = disabled total count, 1 = calculate exact total amount (slow), 2 = calculate only for next page (fast) - * @default 0 - * @enum {integer} - */ - 'total-count-mode'?: 0 | 1 | 2; - }; - CrossSellingElementCollection: { - crossSelling?: { - active?: boolean; - /** Format: int32 */ - limit?: number; - name?: string; - /** Format: int32 */ - position?: number; - productId?: string; - productStreamId?: string; - sortBy?: string; - sortDirection?: string; - type?: string; - }; - products?: components['schemas']['Product'][]; - /** Format: int32 */ - total?: number; - }[]; - /** Added since version: 6.0.0.0 */ - Currency: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - /** Format: float */ - factor: number; - id?: string; - isoCode: string; - /** Runtime field, cannot be used as part of the criteria. */ - isSystemDefault?: boolean; - itemRounding: { - /** Format: int64 */ - decimals?: number; - /** Format: float */ - interval?: number; - roundForNet?: boolean; - }; - name: string; - /** Format: int64 */ - position?: number; - shortName: string; - symbol: string; - /** Format: float */ - taxFreeFrom?: number; - totalRounding: { - /** Format: int64 */ - decimals?: number; - /** Format: float */ - interval?: number; - roundForNet?: boolean; - }; - translated?: { - isoCode?: string; - name?: string; - shortName?: string; - symbol?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.0.0 */ - CurrencyCountryRounding: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CurrencyJsonApi: components['schemas']['resource'] & { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - /** Format: float */ - factor: number; - id?: string; - isoCode: string; - /** Runtime field, cannot be used as part of the criteria. */ - isSystemDefault?: boolean; - itemRounding: { - /** Format: int64 */ - decimals?: number; - /** Format: float */ - interval?: number; - roundForNet?: boolean; - }; - name: string; - /** Format: int64 */ - position?: number; - shortName: string; - symbol: string; - /** Format: float */ - taxFreeFrom?: number; - totalRounding: { - /** Format: int64 */ - decimals?: number; - /** Format: float */ - interval?: number; - roundForNet?: boolean; - }; - translated?: { - isoCode?: string; - name?: string; - shortName?: string; - symbol?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.9.0 */ - CustomEntity: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Customer: { - accountType: string; - active?: boolean; - addresses?: components['schemas']['CustomerAddress']; - affiliateCode?: string; - birthday?: string; - campaignCode?: string; - company?: string; - /** Format: date-time */ - createdAt: string; - createdById?: string; - customerNumber: string; - customFields?: GenericRecord; - defaultBillingAddress?: components['schemas']['CustomerAddress']; - defaultBillingAddressId: string; - defaultPaymentMethod?: components['schemas']['PaymentMethod']; - defaultPaymentMethodId: string; - defaultShippingAddress?: components['schemas']['CustomerAddress']; - defaultShippingAddressId: string; - /** Format: date-time */ - doubleOptInConfirmDate?: string; - /** Format: date-time */ - doubleOptInEmailSentDate?: string; - doubleOptInRegistration?: boolean; - email: string; - /** Format: date-time */ - firstLogin?: string; - firstName: string; - group?: components['schemas']['CustomerGroup']; - groupId: string; - guest?: boolean; - hash?: string; - id?: string; - language?: components['schemas']['Language']; - languageId: string; - /** Format: date-time */ - lastLogin?: string; - lastName: string; - /** Format: date-time */ - lastOrderDate?: string; - lastPaymentMethod?: components['schemas']['PaymentMethod']; - lastPaymentMethodId?: string; - /** Format: int64 */ - orderCount?: number; - /** Format: float */ - orderTotalAmount?: number; - /** Format: int64 */ - reviewCount?: number; - salesChannelId: string; - salutation?: components['schemas']['Salutation']; - salutationId?: string; - tagIds?: readonly string[]; - title?: string; - /** Format: date-time */ - updatedAt?: string; - updatedById?: string; - vatIds?: string[]; - }; - /** Added since version: 6.0.0.0 */ - CustomerAddress: { - additionalAddressLine1?: string; - additionalAddressLine2?: string; - city: string; - company?: string; - country?: components['schemas']['Country']; - countryId: string; - countryState?: components['schemas']['CountryState']; - countryStateId?: string; - /** Format: date-time */ - createdAt: string; - customerId: string; - customFields?: GenericRecord; - department?: string; - firstName: string; - id?: string; - lastName: string; - phoneNumber?: string; - salutation?: components['schemas']['Salutation']; - salutationId?: string; - street: string; - title?: string; - /** Format: date-time */ - updatedAt?: string; - zipcode?: string; - }; - /** Added since version: 6.0.0.0 */ - CustomerGroup: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - displayGross?: boolean; - id?: string; - name: string; - registrationActive?: boolean; - registrationIntroduction?: string; - registrationOnlyCompanyRegistration?: boolean; - registrationSeoMetaDescription?: string; - registrationTitle?: string; - translated?: { - name?: string; - registrationIntroduction?: string; - registrationSeoMetaDescription?: string; - registrationTitle?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.1.0.0 */ - CustomerRecovery: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CustomerTag: { - customerId: string; - id?: string; - tag?: components['schemas']['Tag']; - tagId: string; - }; - /** Added since version: 6.3.4.0 */ - CustomerWishlist: { - /** Format: date-time */ - createdAt: string; - customerId: string; - customFields?: GenericRecord; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.4.0 */ - CustomerWishlistProduct: { - /** Format: date-time */ - createdAt: string; - id?: string; - productId: string; - productVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CustomField: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CustomFieldSet: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - CustomFieldSetRelation: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - CustomPrice: { - /** Format: date-time */ - createdAt: string; - customer?: components['schemas']['Customer']; - customerGroup?: components['schemas']['CustomerGroup']; - customerGroupId?: string; - customerId?: string; - id?: string; - price: GenericRecord; - product?: components['schemas']['Product']; - productId: string; - productVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** The document's "primary data" is a representation of the resource or collection of resources targeted by a request. */ - data: components['schemas']['resource'] | components['schemas']['resource'][]; - /** Added since version: 6.0.0.0 */ - DeliveryTime: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - /** Format: int64 */ - max: number; - /** Format: int64 */ - min: number; - name: string; - translated?: { - name?: string; - unit?: string; - }; - unit: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Document: { - config: GenericRecord; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - deepLinkCode: string; - dependentDocuments?: components['schemas']['Document']; - documentMediaFile?: components['schemas']['Media']; - documentMediaFileId?: string; - documentType?: components['schemas']['DocumentType']; - documentTypeId: string; - fileType: string; - id?: string; - order?: components['schemas']['Order']; - orderId: string; - orderVersionId?: string; - referencedDocument?: components['schemas']['Document']; - referencedDocumentId?: string; - sent?: boolean; - static?: boolean; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - DocumentBaseConfig: { - config?: GenericRecord; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - documentNumber?: string; - documentTypeId: string; - filenamePrefix?: string; - filenameSuffix?: string; - global: boolean; - id?: string; - logo?: components['schemas']['Media']; - logoId?: string; - name: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - DocumentBaseConfigSalesChannel: { - /** Format: date-time */ - createdAt: string; - documentBaseConfigId: string; - documentTypeId?: string; - id?: string; - salesChannelId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - DocumentType: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - technicalName: string; - translated?: { - name?: string; - technicalName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - EntitySearchResult: components['schemas']['ArrayStruct'] & { - /** Contains aggregated data. A simple example is the determination of the average price from a product search query. */ - aggregations?: GenericRecord[]; - entity?: string; - /** The actual limit. This is used for pagination and goes together with the page. */ - limit?: number; - /** The actual page. This can be used for pagination. */ - page?: number; - /** The total number of found entities */ - total?: number; - }; - error: { - /** An application-specific error code, expressed as a string value. */ - code?: string; - /** A human-readable explanation specific to this occurrence of the problem. */ - detail?: string; - /** A unique identifier for this particular occurrence of the problem. */ - id?: string; - links?: components['schemas']['links']; - meta?: components['schemas']['meta']; - source?: { - /** A string indicating which query parameter caused the error. */ - parameter?: string; - /** A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute]. */ - pointer?: string; - }; - /** The HTTP status code applicable to this problem, expressed as a string value. */ - status?: string; - /** A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization. */ - title?: string; - }; - failure: { - errors: components['schemas']['error'][]; - links?: components['schemas']['links']; - meta?: components['schemas']['meta']; - }; - FindProductVariantRouteResponse: { - foundCombination?: { - options?: string[]; - variantId?: string; - }; - }; - /** Added since version: 6.4.6.0 */ - Flow: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.6.0 */ - FlowSequence: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.18.0 */ - FlowTemplate: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ImportExportFile: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ImportExportLog: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ImportExportProfile: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - info: { - jsonapi?: components['schemas']['jsonapi']; - links?: components['schemas']['links']; - meta: components['schemas']['meta']; - }; - /** Added since version: 6.0.0.0 */ - Integration: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** An object describing the server's implementation */ - jsonapi: { - meta?: components['schemas']['meta']; - version?: string; - }; - /** Added since version: 6.4.0.0 */ - LandingPage: { - active?: boolean; - cmsPage?: components['schemas']['CmsPage']; - cmsPageId?: string; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - keywords?: string; - metaDescription?: string; - metaTitle?: string; - name: string; - seoUrls?: components['schemas']['SeoUrl']; - slotConfig?: GenericRecord; - translated?: { - cmsPageId?: string; - cmsPageVersionId?: string; - keywords?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - url?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - url: string; - versionId?: string; - }; - /** Added since version: 6.4.0.0 */ - LandingPageJsonApi: components['schemas']['resource'] & { - active?: boolean; - cmsPageId?: string; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - keywords?: string; - metaDescription?: string; - metaTitle?: string; - name: string; - relationships?: { - cmsPage?: { - data?: { - /** @example 0188f8334a3371508da83536076f5d02 */ - id?: string; - /** @example cms_page */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /landing-page/0188f8334a3371508da835360727da28/cmsPage - */ - related?: string; - }; - }; - seoUrls?: { - data?: { - /** @example 0188f8334a3371508da8353607fb977f */ - id?: string; - /** @example seo_url */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /landing-page/0188f8334a3371508da835360727da28/seoUrls - */ - related?: string; - }; - }; - }; - slotConfig?: GenericRecord; - translated?: { - cmsPageId?: string; - cmsPageVersionId?: string; - keywords?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - url?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - url: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - Language: { - children?: components['schemas']['Language']; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - locale?: components['schemas']['Locale']; - localeId: string; - name: string; - parent?: components['schemas']['Language']; - parentId?: string; - translationCode?: components['schemas']['Locale']; - translationCodeId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - LanguageJsonApi: components['schemas']['resource'] & { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - localeId: string; - name: string; - parentId?: string; - relationships?: { - children?: { - data?: { - /** @example 0188f8334a3471fb97fef3b5dd5bdeef */ - id?: string; - /** @example language */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /language/0188f8334a3471fb97fef3b5dbb3b49f/children - */ - related?: string; - }; - }; - locale?: { - data?: { - /** @example 0188f8334a3471fb97fef3b5dc1432ad */ - id?: string; - /** @example locale */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /language/0188f8334a3471fb97fef3b5dbb3b49f/locale - */ - related?: string; - }; - }; - parent?: { - data?: { - /** @example 0188f8334a3471fb97fef3b5dbbdb24d */ - id?: string; - /** @example language */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /language/0188f8334a3471fb97fef3b5dbb3b49f/parent - */ - related?: string; - }; - }; - translationCode?: { - data?: { - /** @example 0188f8334a3471fb97fef3b5dce52fac */ - id?: string; - /** @example locale */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /language/0188f8334a3471fb97fef3b5dbb3b49f/translationCode - */ - related?: string; - }; - }; - }; - translationCodeId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - LineItem: { - description?: string; - good?: boolean; - id?: string; - label?: string; - modified?: boolean; - /** Format: int32 */ - quantity?: number; - referencedId?: string; - removable?: boolean; - stackable?: boolean; - type?: string; - }; - /** A link **MUST** be represented as either: a string containing the link's URL or a link object. */ - link: OneOf< - [ - string, - { - /** - * Format: uri-reference - * A string containing the link's URL. - */ - href: string; - meta?: components['schemas']['meta']; - } - ] - >; - /** The "type" and "id" to non-empty members. */ - linkage: { - id: string; - meta?: components['schemas']['meta']; - type: string; - }; - links: GenericRecord; - /** Added since version: 6.0.0.0 */ - Locale: { - code: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - territory: string; - translated?: { - code?: string; - name?: string; - territory?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - LogEntry: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MailHeaderFooter: { - /** Format: date-time */ - createdAt: string; - description?: string; - footerHtml?: string; - footerPlain?: string; - headerHtml?: string; - headerPlain?: string; - id?: string; - name: string; - systemDefault?: boolean; - translated?: { - description?: string; - footerHtml?: string; - footerPlain?: string; - headerHtml?: string; - headerPlain?: string; - name?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MailTemplate: { - contentHtml: string; - contentPlain: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - mailTemplateType?: components['schemas']['MailTemplateType']; - media?: components['schemas']['MailTemplateMedia']; - senderName?: string; - systemDefault?: boolean; - translated?: { - contentHtml?: string; - contentPlain?: string; - senderName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MailTemplateMedia: { - id?: string; - languageId: string; - mailTemplateId: string; - media?: components['schemas']['Media']; - mediaId: string; - /** Format: int64 */ - position?: number; - }; - /** Added since version: 6.0.0.0 */ - MailTemplateType: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - technicalName: string; - translated?: { - name?: string; - technicalName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.1.0.0 */ - MainCategory: { - categoryId: string; - categoryVersionId?: string; - /** Format: date-time */ - createdAt: string; - id?: string; - productId: string; - productVersionId?: string; - salesChannelId: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.1.0.0 */ - MainCategoryJsonApi: components['schemas']['resource'] & { - categoryId: string; - categoryVersionId?: string; - /** Format: date-time */ - createdAt: string; - id?: string; - productId: string; - productVersionId?: string; - salesChannelId: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Media: { - alt?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - extensions?: { - mediaAiTag?: { - data?: { - /** @example 0188f8334a3670679577cca714656bdf */ - id?: string; - /** @example media_ai_tag */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /media/0188f8334a3670679577cca713097429/mediaAiTag - */ - related?: string; - }; - }; - }; - fileExtension?: string; - fileName?: string; - /** Format: int64 */ - fileSize?: number; - /** Runtime field, cannot be used as part of the criteria. */ - hasFile?: boolean; - id?: string; - metaData?: GenericRecord; - mimeType?: string; - private?: boolean; - thumbnails?: components['schemas']['MediaThumbnail']; - title?: string; - translated?: { - alt?: string; - fileExtension?: string; - fileName?: string; - mimeType?: string; - title?: string; - uploadedAt?: string; - url?: string; - }; - /** Format: date-time */ - updatedAt?: string; - /** Format: date-time */ - uploadedAt?: string; - /** Runtime field, cannot be used as part of the criteria. */ - url?: string; - }; - /** Added since version: */ - MediaAiTag: { - /** Format: date-time */ - createdAt: string; - id?: string; - media?: components['schemas']['Media']; - tags?: unknown[]; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MediaDefaultFolder: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MediaFolder: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MediaFolderConfiguration: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - MediaTag: { - id?: string; - media?: components['schemas']['Media']; - mediaId: string; - tag?: components['schemas']['Tag']; - tagId: string; - }; - /** Added since version: 6.0.0.0 */ - MediaThumbnail: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - /** Format: int64 */ - height: number; - id?: string; - mediaId: string; - /** Format: date-time */ - updatedAt?: string; - /** Runtime field, cannot be used as part of the criteria. */ - url?: string; - /** Format: int64 */ - width: number; - }; - /** Added since version: 6.0.0.0 */ - MediaThumbnailSize: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - /** Format: int64 */ - height: number; - id?: string; - /** Format: date-time */ - updatedAt?: string; - /** Format: int64 */ - width: number; - }; - /** Non-standard meta-information that can not be represented as an attribute or relationship. */ - meta: GenericRecord; - NavigationRouteResponse: components['schemas']['Category'][]; - /** Added since version: 6.0.0.0 */ - NewsletterRecipient: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - NewsletterRecipientJsonApi: components['schemas']['resource'] & { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.7.0 */ - Notification: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - NumberRange: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - NumberRangeSalesChannel: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - NumberRangeState: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - NumberRangeType: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Order: { - addresses?: components['schemas']['OrderAddress']; - affiliateCode?: string; - /** Format: float */ - amountNet?: number; - /** Format: float */ - amountTotal?: number; - billingAddress?: components['schemas']['OrderAddress']; - billingAddressId: string; - billingAddressVersionId?: string; - campaignCode?: string; - /** Format: date-time */ - createdAt: string; - createdById?: string; - currency?: components['schemas']['Currency']; - /** Format: float */ - currencyFactor: number; - currencyId: string; - customerComment?: string; - customFields?: GenericRecord; - deepLinkCode?: string; - deliveries?: components['schemas']['OrderDelivery']; - documents?: components['schemas']['Document']; - extensions?: { - returns?: { - data?: { - /** @example 0188f8334a3971cabdd5f601ba4bcdc4 */ - id?: string; - /** @example order_return */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /order/0188f8334a387386ab9b000ef2e2f216/returns - */ - related?: string; - }; - }; - }; - id?: string; - language?: components['schemas']['Language']; - languageId: string; - lineItems?: components['schemas']['OrderLineItem']; - orderCustomer?: components['schemas']['OrderCustomer']; - orderDate?: string; - /** Format: date-time */ - orderDateTime: string; - orderNumber?: string; - /** Format: float */ - positionPrice?: number; - price?: { - calculatedTaxes?: GenericRecord; - /** Format: float */ - netPrice: number; - /** Format: float */ - positionPrice: number; - /** Format: float */ - rawTotal: number; - taxRules?: GenericRecord; - taxStatus: string; - /** Format: float */ - totalPrice: number; - }; - salesChannelId: string; - shippingCosts?: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - /** Format: float */ - shippingTotal?: number; - stateMachineState?: components['schemas']['StateMachineState']; - tags?: components['schemas']['Tag']; - taxStatus?: string; - transactions?: components['schemas']['OrderTransaction']; - /** Format: date-time */ - updatedAt?: string; - updatedById?: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - OrderAddress: { - additionalAddressLine1?: string; - additionalAddressLine2?: string; - city: string; - company?: string; - country?: components['schemas']['Country']; - countryId: string; - countryState?: components['schemas']['CountryState']; - countryStateId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - department?: string; - firstName: string; - id?: string; - lastName: string; - phoneNumber?: string; - salutation?: components['schemas']['Salutation']; - street: string; - title?: string; - /** Format: date-time */ - updatedAt?: string; - vatId?: string; - versionId?: string; - zipcode?: string; - }; - /** Added since version: 6.0.0.0 */ - OrderCustomer: { - company?: string; - /** Format: date-time */ - createdAt: string; - customerNumber?: string; - customFields?: GenericRecord; - email: string; - firstName: string; - id?: string; - lastName: string; - salutation?: components['schemas']['Salutation']; - salutationId: string; - title?: string; - /** Format: date-time */ - updatedAt?: string; - vatIds?: string[]; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - OrderDelivery: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - orderId: string; - orderVersionId?: string; - positions?: components['schemas']['OrderDeliveryPosition']; - shippingCosts?: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - /** Format: date-time */ - shippingDateEarliest: string; - /** Format: date-time */ - shippingDateLatest: string; - shippingMethod?: components['schemas']['ShippingMethod']; - shippingMethodId: string; - shippingOrderAddress?: components['schemas']['OrderAddress']; - shippingOrderAddressId: string; - shippingOrderAddressVersionId?: string; - stateId: string; - stateMachineState?: components['schemas']['StateMachineState']; - trackingCodes: string[]; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - OrderDeliveryPosition: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - orderDeliveryId: string; - orderDeliveryVersionId?: string; - orderLineItemId: string; - orderLineItemVersionId?: string; - price?: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - /** Format: int64 */ - quantity?: number; - /** Format: float */ - totalPrice?: number; - /** Format: float */ - unitPrice?: number; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - OrderLineItem: { - children: components['schemas']['OrderLineItem']; - cover?: components['schemas']['Media']; - coverId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - downloads?: components['schemas']['OrderLineItemDownload']; - extensions?: { - returns?: { - data?: { - /** @example 0188f8334a3b72a794436c60aa753810 */ - id?: string; - /** @example order_return_line_item */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /order-line-item/0188f8334a3b72a794436c60a7f605e7/returns - */ - related?: string; - }; - }; - state?: { - data?: { - /** @example 0188f8334a3b72a794436c60aad25086 */ - id?: string; - /** @example state_machine_state */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /order-line-item/0188f8334a3b72a794436c60a7f605e7/state - */ - related?: string; - }; - }; - }; - good?: boolean; - id?: string; - identifier: string; - label: string; - orderDeliveryPositions?: components['schemas']['OrderDeliveryPosition']; - orderId: string; - orderVersionId?: string; - parent?: components['schemas']['OrderLineItem']; - parentId?: string; - parentVersionId?: string; - payload?: GenericRecord; - /** Format: int64 */ - position: number; - priceDefinition?: GenericRecord; - productId?: string; - productVersionId?: string; - /** Format: int64 */ - quantity: number; - referencedId?: string; - removable?: boolean; - stackable?: boolean; - states: string[]; - /** Format: float */ - totalPrice?: number; - type?: string; - /** Format: float */ - unitPrice?: number; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.4.19.0 */ - OrderLineItemDownload: { - accessGranted: boolean; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - media?: components['schemas']['Media']; - mediaId: string; - orderLineItem?: components['schemas']['OrderLineItem']; - orderLineItemId: string; - orderLineItemVersionId?: string; - /** Format: int64 */ - position: number; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - OrderProductWarehouse: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - OrderReturn: { - /** Format: float */ - amountNet?: number; - /** Format: float */ - amountTotal?: number; - /** Format: date-time */ - createdAt: string; - createdById?: string; - id?: string; - lineItems?: components['schemas']['OrderReturnLineItem']; - orderId: string; - orderVersionId?: string; - price?: { - calculatedTaxes?: GenericRecord; - /** Format: float */ - netPrice: number; - /** Format: float */ - positionPrice: number; - /** Format: float */ - rawTotal: number; - taxRules?: GenericRecord; - taxStatus: string; - /** Format: float */ - totalPrice: number; - }; - /** Format: date-time */ - requestedAt: string; - returnNumber: string; - shippingCosts?: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - state?: components['schemas']['StateMachineState']; - stateId: string; - /** Format: date-time */ - updatedAt?: string; - updatedById?: string; - versionId?: string; - }; - /** Added since version: */ - OrderReturnLineItem: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - orderLineItemId: string; - orderLineItemVersionId?: string; - orderReturnId: string; - orderReturnVersionId?: string; - /** Format: int64 */ - quantity: number; - reason?: components['schemas']['OrderReturnLineItemReason']; - reasonId: string; - /** Format: float */ - refundAmount?: number; - /** Format: int64 */ - restockQuantity?: number; - state?: components['schemas']['StateMachineState']; - stateId: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - OrderReturnLineItemReason: { - content: string; - /** Format: date-time */ - createdAt: string; - id?: string; - reasonKey: string; - translated?: { - content?: string; - reasonKey?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - OrderRouteResponse: { - orders?: Record; - /** The key-value pairs contain the uuid of the order as key and a boolean as value, indicating that the payment method can still be changed. */ - paymentChangeable?: GenericRecord; - }; - /** Added since version: 6.0.0.0 */ - OrderTag: { - id?: string; - order?: components['schemas']['Order']; - orderId: string; - orderVersionId?: string; - tag?: components['schemas']['Tag']; - tagId: string; - }; - /** Added since version: 6.0.0.0 */ - OrderTransaction: { - amount: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - captures?: components['schemas']['OrderTransactionCapture']; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - orderId: string; - orderVersionId?: string; - paymentMethod?: components['schemas']['PaymentMethod']; - paymentMethodId: string; - stateId: string; - stateMachineState?: components['schemas']['StateMachineState']; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.4.12.0 */ - OrderTransactionCapture: { - amount: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - externalReference?: string; - id?: string; - orderTransactionId: string; - orderTransactionVersionId?: string; - refunds?: components['schemas']['OrderTransactionCaptureRefund']; - stateId: string; - stateMachineState?: components['schemas']['StateMachineState']; - transaction?: components['schemas']['OrderTransaction']; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.12.0 */ - OrderTransactionCaptureRefund: { - amount: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - captureId: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - externalReference?: string; - id?: string; - positions?: components['schemas']['OrderTransactionCaptureRefundPosition']; - reason?: string; - stateId: string; - stateMachineState?: components['schemas']['StateMachineState']; - transactionCapture?: components['schemas']['OrderTransactionCapture']; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.12.0 */ - OrderTransactionCaptureRefundPosition: { - amount: { - calculatedTaxes?: GenericRecord; - listPrice?: { - /** Format: float */ - discount?: number; - /** Format: float */ - percentage?: number; - /** Format: float */ - price?: number; - }; - /** Format: int64 */ - quantity: number; - referencePrice?: GenericRecord; - regulationPrice?: { - /** Format: float */ - price?: number; - }; - taxRules?: GenericRecord; - /** Format: float */ - totalPrice: number; - /** Format: float */ - unitPrice: number; - }; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - externalReference?: string; - id?: string; - orderLineItem?: components['schemas']['OrderLineItem']; - orderLineItemId: string; - orderLineItemVersionId?: string; - orderTransactionCaptureRefund?: components['schemas']['OrderTransactionCaptureRefund']; - /** Format: int64 */ - quantity?: number; - reason?: string; - refundId: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - OrderWarehouseGroup: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - pagination: { - /** - * Format: uri-reference - * The first page of data - */ - first?: string; - /** - * Format: uri-reference - * The last page of data - */ - last?: string; - /** - * Format: uri-reference - * The next page of data - */ - next?: string; - /** - * Format: uri-reference - * The previous page of data - */ - prev?: string; - }; - /** Added since version: 6.0.0.0 */ - PaymentMethod: { - active?: boolean; - afterOrderEnabled?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - asynchronous?: boolean; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - distinguishableName?: string; - id?: string; - media?: components['schemas']['Media']; - mediaId?: string; - name: string; - /** Format: int64 */ - position?: number; - /** Runtime field, cannot be used as part of the criteria. */ - prepared?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - refundable?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - shortName?: string; - /** Runtime field, cannot be used as part of the criteria. */ - synchronous?: boolean; - translated?: { - description?: string; - distinguishableName?: string; - mediaId?: string; - name?: string; - shortName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PaymentMethodJsonApi: components['schemas']['resource'] & { - active?: boolean; - afterOrderEnabled?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - asynchronous?: boolean; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - distinguishableName?: string; - id?: string; - mediaId?: string; - name: string; - /** Format: int64 */ - position?: number; - /** Runtime field, cannot be used as part of the criteria. */ - prepared?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - refundable?: boolean; - relationships?: { - media?: { - data?: { - /** @example 0188f8334a3e72eaac34aa0e8fdba60c */ - id?: string; - /** @example media */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /payment-method/0188f8334a3e72eaac34aa0e8f0d11e9/media - */ - related?: string; - }; - }; - }; - /** Runtime field, cannot be used as part of the criteria. */ - shortName?: string; - /** Runtime field, cannot be used as part of the criteria. */ - synchronous?: boolean; - translated?: { - description?: string; - distinguishableName?: string; - mediaId?: string; - name?: string; - shortName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Plugin: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Product: { - active?: boolean; - available?: boolean; - /** Format: int64 */ - availableStock?: number; - calculatedCheapestPrice?: GenericRecord; - /** - * Format: int64 - * Runtime field, cannot be used as part of the criteria. - */ - calculatedMaxPurchase?: number; - calculatedPrice?: GenericRecord; - calculatedPrices?: unknown[]; - canonicalProduct?: components['schemas']['Product']; - canonicalProductId?: string; - categories?: components['schemas']['Category']; - categoriesRo?: components['schemas']['Category']; - categoryIds?: readonly string[]; - categoryTree?: readonly string[]; - /** Format: int64 */ - childCount?: number; - children?: components['schemas']['Product'][]; - cmsPage?: components['schemas']['CmsPage']; - cmsPageId?: string; - cmsPageVersionId?: string; - configuratorSettings?: components['schemas']['ProductConfiguratorSetting']; - cover?: components['schemas']['ProductMedia']; - coverId?: string; - /** Format: date-time */ - createdAt: string; - crossSellings?: components['schemas']['ProductCrossSelling']; - customFields?: GenericRecord; - deliveryTime?: components['schemas']['DeliveryTime']; - deliveryTimeId?: string; - description?: string; - displayGroup?: string; - downloads?: components['schemas']['ProductDownload']; - ean?: string; - extensions?: { - reviewSummaries?: { - data?: { - /** @example 0188f8334a407238b554caed891d5bf9 */ - id?: string; - /** @example product_review_summary */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/reviewSummaries - */ - related?: string; - }; - }; - swagCustomizedProductsTemplate?: { - data?: { - /** @example 0188f8334a407238b554caed8958c5e7 */ - id?: string; - /** @example swag_customized_products_template */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/swagCustomizedProductsTemplate - */ - related?: string; - }; - }; - }; - /** Format: float */ - height?: number; - id?: string; - isCloseout?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - isNew?: boolean; - keywords?: string; - /** Format: float */ - length?: number; - mainCategories?: components['schemas']['MainCategory']; - manufacturer?: components['schemas']['ProductManufacturer']; - manufacturerId?: string; - manufacturerNumber?: string; - markAsTopseller?: boolean; - /** Format: int64 */ - maxPurchase?: number; - media?: Array; - metaDescription?: string; - metaTitle?: string; - /** Format: int64 */ - minPurchase?: number; - name: string; - optionIds?: readonly string[]; - options?: components['schemas']['PropertyGroupOption'][]; - packUnit?: string; - packUnitPlural?: string; - parent?: components['schemas']['Product']; - parentId?: string; - parentVersionId?: string; - productManufacturerVersionId?: string; - productMediaVersionId?: string; - productNumber: string; - productReviews?: components['schemas']['ProductReview']; - properties?: components['schemas']['PropertyGroupOption']; - propertyIds?: readonly string[]; - /** Format: int64 */ - purchaseSteps?: number; - /** Format: float */ - purchaseUnit?: number; - /** Format: float */ - ratingAverage?: number; - /** Format: float */ - referenceUnit?: number; - /** Format: date-time */ - releaseDate?: string; - /** Format: int64 */ - restockTime?: number; - /** Format: int64 */ - sales?: number; - seoCategory?: components['schemas']['Category']; - seoUrls?: Array; - shippingFree?: boolean; - sortedProperties?: GenericRecord; - states?: readonly string[]; - /** Format: int64 */ - stock: number; - streamIds?: readonly string[]; - streams?: components['schemas']['ProductStream']; - tax?: components['schemas']['Tax']; - taxId: string; - translated?: { - canonicalProductId?: string; - cmsPageId?: string; - cmsPageVersionId?: string; - coverId?: string; - deliveryTimeId?: string; - description?: string; - displayGroup?: string; - ean?: string; - keywords?: string; - manufacturerId?: string; - manufacturerNumber?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - packUnit?: string; - packUnitPlural?: string; - parentId?: string; - parentVersionId?: string; - productManufacturerVersionId?: string; - productMediaVersionId?: string; - productNumber?: string; - releaseDate?: string; - taxId?: string; - unitId?: string; - versionId?: string; - }; - unit?: components['schemas']['Unit']; - unitId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - /** Format: float */ - weight?: number; - /** Format: float */ - width?: number; - }; - /** Added since version: 6.0.0.0 */ - ProductConfiguratorSetting: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - media?: components['schemas']['Media']; - mediaId?: string; - option?: components['schemas']['PropertyGroupOption']; - optionId: string; - /** Format: int64 */ - position?: number; - productId: string; - productVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.1.0.0 */ - ProductCrossSelling: { - active?: boolean; - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: int64 */ - limit?: number; - name: string; - /** Format: int64 */ - position: number; - sortBy?: string; - sortDirection?: string; - translated?: { - name?: string; - sortBy?: string; - sortDirection?: string; - type?: string; - }; - type: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.2.0.0 */ - ProductCrossSellingAssignedProducts: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Represents a product along with detailed information required to display a variant selection. */ - ProductDetailResponse: { - /** List of property groups with their corresponding options and information on how to display them. */ - configurator?: components['schemas']['PropertyGroup'][]; - product?: components['schemas']['Product']; - }; - /** Added since version: 6.4.19.0 */ - ProductDownload: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - media?: components['schemas']['Media']; - mediaId: string; - /** Format: int64 */ - position?: number; - product?: components['schemas']['Product']; - productId: string; - productVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.1.0.0 */ - ProductExport: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.0.0 */ - ProductFeatureSet: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductJsonApi: components['schemas']['resource'] & { - active?: boolean; - available?: boolean; - /** Format: int64 */ - availableStock?: number; - calculatedCheapestPrice?: GenericRecord; - /** - * Format: int64 - * Runtime field, cannot be used as part of the criteria. - */ - calculatedMaxPurchase?: number; - calculatedPrice?: GenericRecord; - calculatedPrices?: unknown[]; - canonicalProductId?: string; - categoryIds?: readonly string[]; - categoryTree?: readonly string[]; - /** Format: int64 */ - childCount?: number; - cmsPageId?: string; - cmsPageVersionId?: string; - coverId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - deliveryTimeId?: string; - description?: string; - displayGroup?: string; - ean?: string; - extensions?: { - reviewSummaries?: { - data?: { - /** @example 0188f8334a407238b554caed891d5bf9 */ - id?: string; - /** @example product_review_summary */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/reviewSummaries - */ - related?: string; - }; - }; - swagCustomizedProductsTemplate?: { - data?: { - /** @example 0188f8334a407238b554caed8958c5e7 */ - id?: string; - /** @example swag_customized_products_template */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/swagCustomizedProductsTemplate - */ - related?: string; - }; - }; - }; - /** Format: float */ - height?: number; - id?: string; - isCloseout?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - isNew?: boolean; - keywords?: string; - /** Format: float */ - length?: number; - manufacturerId?: string; - manufacturerNumber?: string; - markAsTopseller?: boolean; - /** Format: int64 */ - maxPurchase?: number; - metaDescription?: string; - metaTitle?: string; - /** Format: int64 */ - minPurchase?: number; - name: string; - optionIds?: readonly string[]; - packUnit?: string; - packUnitPlural?: string; - parentId?: string; - parentVersionId?: string; - productManufacturerVersionId?: string; - productMediaVersionId?: string; - productNumber: string; - propertyIds?: readonly string[]; - /** Format: int64 */ - purchaseSteps?: number; - /** Format: float */ - purchaseUnit?: number; - /** Format: float */ - ratingAverage?: number; - /** Format: float */ - referenceUnit?: number; - relationships?: { - canonicalProduct?: { - data?: { - /** @example 0188f8334a407238b554caed828d5d76 */ - id?: string; - /** @example product */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/canonicalProduct - */ - related?: string; - }; - }; - categories?: { - data?: { - /** @example 0188f8334a407238b554caed86f22aeb */ - id?: string; - /** @example category */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/categories - */ - related?: string; - }; - }; - categoriesRo?: { - data?: { - /** @example 0188f8334a407238b554caed87f3c986 */ - id?: string; - /** @example category */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/categoriesRo - */ - related?: string; - }; - }; - children?: { - data?: { - /** @example 0188f8334a3f7253b847b4936f987d95 */ - id?: string; - /** @example product */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/children - */ - related?: string; - }; - }; - cmsPage?: { - data?: { - /** @example 0188f8334a3f7253b847b49373864110 */ - id?: string; - /** @example cms_page */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/cmsPage - */ - related?: string; - }; - }; - configuratorSettings?: { - data?: { - /** @example 0188f8334a407238b554caed84e10090 */ - id?: string; - /** @example product_configurator_setting */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/configuratorSettings - */ - related?: string; - }; - }; - cover?: { - data?: { - /** @example 0188f8334a3f7253b847b493729736ca */ - id?: string; - /** @example product_media */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/cover - */ - related?: string; - }; - }; - crossSellings?: { - data?: { - /** @example 0188f8334a407238b554caed83f60d79 */ - id?: string; - /** @example product_cross_selling */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/crossSellings - */ - related?: string; - }; - }; - deliveryTime?: { - data?: { - /** @example 0188f8334a3f7253b847b493701a96ea */ - id?: string; - /** @example delivery_time */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/deliveryTime - */ - related?: string; - }; - }; - downloads?: { - data?: { - /** @example 0188f8334a3f7253b847b4936f08f801 */ - id?: string; - /** @example product_download */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/downloads - */ - related?: string; - }; - }; - mainCategories?: { - data?: { - /** @example 0188f8334a407238b554caed8593aecf */ - id?: string; - /** @example main_category */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/mainCategories - */ - related?: string; - }; - }; - manufacturer?: { - data?: { - /** @example 0188f8334a3f7253b847b493712fbfc9 */ - id?: string; - /** @example product_manufacturer */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/manufacturer - */ - related?: string; - }; - }; - media?: { - data?: { - /** @example 0188f8334a407238b554caed832b9d78 */ - id?: string; - /** @example product_media */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/media - */ - related?: string; - }; - }; - options?: { - data?: { - /** @example 0188f8334a407238b554caed86a31928 */ - id?: string; - /** @example property_group_option */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/options - */ - related?: string; - }; - }; - parent?: { - data?: { - /** @example 0188f8334a3f7253b847b4936f97cdbd */ - id?: string; - /** @example product */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/parent - */ - related?: string; - }; - }; - productReviews?: { - data?: { - /** @example 0188f8334a407238b554caed84f7846e */ - id?: string; - /** @example product_review */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/productReviews - */ - related?: string; - }; - }; - properties?: { - data?: { - /** @example 0188f8334a407238b554caed86e4ee70 */ - id?: string; - /** @example property_group_option */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/properties - */ - related?: string; - }; - }; - seoCategory?: { - data?: { - /** @example 0188f8334a407238b554caed886084b2 */ - id?: string; - /** @example category */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/seoCategory - */ - related?: string; - }; - }; - seoUrls?: { - data?: { - /** @example 0188f8334a407238b554caed85b35097 */ - id?: string; - /** @example seo_url */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/seoUrls - */ - related?: string; - }; - }; - streams?: { - data?: { - /** @example 0188f8334a407238b554caed877212b4 */ - id?: string; - /** @example product_stream */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/streams - */ - related?: string; - }; - }; - tax?: { - data?: { - /** @example 0188f8334a3f7253b847b49370fb0a4b */ - id?: string; - /** @example tax */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/tax - */ - related?: string; - }; - }; - unit?: { - data?: { - /** @example 0188f8334a3f7253b847b493720b964c */ - id?: string; - /** @example unit */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /product/0188f8334a3e72eaac34aa0e9089a538/unit - */ - related?: string; - }; - }; - }; - /** Format: date-time */ - releaseDate?: string; - /** Format: int64 */ - restockTime?: number; - /** Format: int64 */ - sales?: number; - shippingFree?: boolean; - sortedProperties?: GenericRecord; - states?: readonly string[]; - /** Format: int64 */ - stock: number; - streamIds?: readonly string[]; - taxId: string; - translated?: { - canonicalProductId?: string; - cmsPageId?: string; - cmsPageVersionId?: string; - coverId?: string; - deliveryTimeId?: string; - description?: string; - displayGroup?: string; - ean?: string; - keywords?: string; - manufacturerId?: string; - manufacturerNumber?: string; - metaDescription?: string; - metaTitle?: string; - name?: string; - packUnit?: string; - packUnitPlural?: string; - parentId?: string; - parentVersionId?: string; - productManufacturerVersionId?: string; - productMediaVersionId?: string; - productNumber?: string; - releaseDate?: string; - taxId?: string; - unitId?: string; - versionId?: string; - }; - unitId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - /** Format: float */ - weight?: number; - /** Format: float */ - width?: number; - }; - /** Added since version: 6.0.0.0 */ - ProductKeywordDictionary: { - id?: string; - keyword: string; - languageId: string; - }; - ProductListingCriteria: components['schemas']['Criteria'] & { - /** Number of items per result page. If not set, the limit will be set according to the default products per page, defined in the system settings. */ - limit?: number; - /** Filter by manufacturers. List of manufacturer identifiers separated by a `|`. */ - manufacturer?: string; - /** - * Enables/disabled filtering by manufacturer. If set to false, the `manufacturer` filter will be ignored. Also the `aggregations[manufacturer]` key will be removed from the response. - * @default true - */ - 'manufacturer-filter'?: boolean; - /** - * Filters by a maximum product price. Has to be higher than the `min-price` filter. - * @default 0 - */ - 'max-price'?: number; - /** - * Filters by a minimum product price. Has to be lower than the `max-price` filter. - * @default 0 - */ - 'min-price'?: number; - /** Specifies the sorting of the products by `availableSortings`. If not set, the default sorting will be set according to the shop settings. The available sorting options are sent within the response under the `availableSortings` key. In order to sort by a field, consider using the `sort` parameter from the listing criteria. Do not use both parameters together, as it might lead to unexpected results. */ - order?: string; - /** - * Search result page - * @default 1 - */ - p?: number; - /** - * Enables/disabled filtering by price. If set to false, the `min-price` and `max-price` filter will be ignored. Also the `aggregations[price]` key will be removed from the response. - * @default true - */ - 'price-filter'?: boolean; - /** Filters products by their properties. List of property identifiers separated by a `|`. */ - properties?: string; - /** - * Enables/disabled filtering by properties products. If set to false, the `properties` filter will be ignored. Also the `aggregations[properties]` key will be removed from the response. - * @default true - */ - 'property-filter'?: boolean; - /** A whitelist of property identifiers which can be used for filtering. List of property identifiers separated by a `|`. The `property-filter` must be `true`, otherwise the whitelist has no effect. */ - 'property-whitelist'?: string; - /** Filter products with a minimum average rating. */ - rating?: number; - /** - * Enables/disabled filtering by rating. If set to false, the `rating` filter will be ignored. Also the `aggregations[rating]` key will be removed from the response. - * @default true - */ - 'rating-filter'?: boolean; - /** By sending the parameter `reduce-aggregations` , the post-filters that were applied by the customer, are also applied to the aggregations. This has the consequence that only values are returned in the aggregations that would lead to further filter results. This parameter is a flag, the value has no effect. */ - 'reduce-aggregations'?: string | null; - /** - * Filters products that are marked as shipping-free. - * @default false - */ - 'shipping-free'?: boolean; - /** - * Enables/disabled filtering by shipping-free products. If set to false, the `shipping-free` filter will be ignored. Also the `aggregations[shipping-free]` key will be removed from the response. - * @default true - */ - 'shipping-free-filter'?: boolean; - }; - /** Additional flags for product listings */ - ProductListingFlags: { - /** Resets all aggregations in the criteria. This parameter is a flag, the value has no effect. */ - 'no-aggregations'?: string | null; - /** If this flag is set, no products are fetched. Sorting and associations are also ignored. This parameter is a flag, the value has no effect. */ - 'only-aggregations'?: string | null; - }; - ProductListingResult: components['schemas']['EntitySearchResult'] & { - /** Contains the available sorting. These can be used to show a sorting select-box in the product listing. */ - availableSortings?: GenericRecord[]; - /** Contains the state of the filters. These can be used to create listing filters. */ - currentFilters?: { - manufacturer?: GenericRecord[]; - navigationId?: string; - price?: { - max?: number; - min?: number; - }; - properties?: GenericRecord[]; - rating?: number; - 'shipping-free'?: boolean; - }; - elements?: components['schemas']['Product'][]; - sorting?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductManufacturer: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - id?: string; - link?: string; - media?: components['schemas']['Media']; - mediaId?: string; - name: string; - translated?: { - description?: string; - link?: string; - mediaId?: string; - name?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductMedia: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - media?: components['schemas']['Media']; - mediaId: string; - /** Format: int64 */ - position?: number; - productId: string; - productVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductPrice: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductReview: { - comment?: string; - content: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - languageId: string; - /** Format: float */ - points?: number; - productId: string; - productVersionId?: string; - salesChannelId: string; - status?: boolean; - title: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - ProductReviewSummary: { - /** Format: date-time */ - createdAt: string; - id?: string; - product?: components['schemas']['Product']; - productId: string; - salesChannel?: components['schemas']['SalesChannel']; - salesChannelId: string; - summary?: string; - translated?: { - productId?: string; - salesChannelId?: string; - summary?: string; - }; - /** Format: date-time */ - updatedAt?: string; - visible?: boolean; - }; - /** Added since version: 6.3.5.0 */ - ProductSearchConfig: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.5.0 */ - ProductSearchConfigField: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductSearchKeyword: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.2.0 */ - ProductSorting: { - /** Format: date-time */ - createdAt: string; - id?: string; - key: string; - label: string; - /** Format: int64 */ - priority: number; - translated?: { - key?: string; - label?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductStream: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - id?: string; - name: string; - translated?: { - description?: string; - name?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductStreamFilter: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ProductVisibility: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - ProductWarehouse: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Promotion: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PromotionDiscount: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PromotionDiscountPrices: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PromotionIndividualCode: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PromotionSalesChannel: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PromotionSetgroup: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - PropertyGroup: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - displayType: string; - filterable?: boolean; - id?: string; - name: string; - options?: components['schemas']['PropertyGroupOption']; - /** Format: int64 */ - position?: number; - sortingType: string; - translated?: { - description?: string; - displayType?: string; - name?: string; - sortingType?: string; - }; - /** Format: date-time */ - updatedAt?: string; - visibleOnProductDetailPage?: boolean; - }; - /** Added since version: 6.0.0.0 */ - PropertyGroupOption: { - colorHexCode?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - group?: components['schemas']['PropertyGroup']; - groupId: string; - id?: string; - media?: components['schemas']['Media']; - mediaId?: string; - name: string; - /** Format: int64 */ - position?: number; - translated?: { - colorHexCode?: string; - groupId?: string; - mediaId?: string; - name?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** A resource object **MAY** contain references to other resource objects ("relationships"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object. */ - relationshipLinks: { - related?: components['schemas']['link']; - self?: GenericRecord[] & components['schemas']['link']; - [key: string]: unknown; - }; - /** Members of the relationships object ("relationships") represent references from the resource object in which it's defined to other resource objects. */ - relationships: unknown; - /** An array of objects each containing \"type\" and \"id\" members for to-many relationships. */ - relationshipToMany: components['schemas']['linkage'][]; - relationshipToOne: components['schemas']['linkage']; - /** "Resource objects" appear in a JSON API document to represent resources. */ - resource: { - attributes?: components['schemas']['attributes']; - id: string; - links?: components['schemas']['links']; - meta?: components['schemas']['meta']; - relationships?: components['schemas']['relationships']; - type: string; - }; - /** Added since version: 6.0.0.0 */ - Rule: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - extensions?: { - warehouseGroup?: { - data?: { - /** @example 0188f8334a4672a58b03a3d55bfc502f */ - id?: string; - /** @example warehouse_group */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /rule/0188f8334a4672a58b03a3d55bb70070/warehouseGroup - */ - related?: string; - }; - }; - }; - id?: string; - name: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - RuleCondition: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SaasAppStorefrontConfig: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SaasSbpUserData: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SaasStorefrontDemoToken: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SaasUserLoginToken: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - SalesChannel: { - active?: boolean; - configuration?: GenericRecord; - country?: components['schemas']['Country']; - countryId: string; - /** Format: date-time */ - createdAt: string; - currency?: components['schemas']['Currency']; - currencyId: string; - customerGroupId: string; - customFields?: GenericRecord; - domains?: components['schemas']['SalesChannelDomain']; - footerCategory?: components['schemas']['Category']; - footerCategoryId?: string; - footerCategoryVersionId?: string; - hreflangActive?: boolean; - hreflangDefaultDomain?: components['schemas']['SalesChannelDomain']; - hreflangDefaultDomainId?: string; - id?: string; - language?: components['schemas']['Language']; - languageId: string; - mailHeaderFooterId?: string; - maintenance?: boolean; - name: string; - navigationCategory?: components['schemas']['Category']; - /** Format: int64 */ - navigationCategoryDepth?: number; - navigationCategoryId: string; - navigationCategoryVersionId?: string; - paymentMethod?: components['schemas']['PaymentMethod']; - paymentMethodId: string; - serviceCategory?: components['schemas']['Category']; - serviceCategoryId?: string; - serviceCategoryVersionId?: string; - shippingMethod?: components['schemas']['ShippingMethod']; - shippingMethodId: string; - shortName?: string; - taxCalculationType?: string; - translated?: { - countryId?: string; - currencyId?: string; - customerGroupId?: string; - footerCategoryId?: string; - footerCategoryVersionId?: string; - hreflangDefaultDomainId?: string; - languageId?: string; - mailHeaderFooterId?: string; - name?: string; - navigationCategoryId?: string; - navigationCategoryVersionId?: string; - paymentMethodId?: string; - serviceCategoryId?: string; - serviceCategoryVersionId?: string; - shippingMethodId?: string; - shortName?: string; - taxCalculationType?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.2.0.0 */ - SalesChannelAnalytics: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - SalesChannelContext: components['schemas']['ArrayStruct'] & { - /** Core context with general configuration values and state */ - context?: { - currencyFactor?: number; - currencyId?: string; - /** Format: int32 */ - currencyPrecision?: number; - scope?: string; - source?: string; - taxState?: string; - useCache?: boolean; - versionId?: string; - }; - /** Currency associated with the current user */ - currency?: { - /** Format: int32 */ - decimalPrecision?: number; - factor?: number; - isoCode?: string; - isSystemDefault?: boolean; - name?: string; - /** Format: int32 */ - position?: number; - shortName?: string; - symbol?: string; - }; - /** Customer group of the current user */ - currentCustomerGroup?: { - displayGross?: boolean; - name?: string; - }; - /** Information about the current customer - `null` if the customer is not logged in */ - customer?: { - active?: boolean; - affiliateCode?: string; - /** Format: int32 */ - autoIncrement?: number; - /** Format: date-time */ - birthday?: string; - campaignCode?: string; - company?: string; - customerNumber?: string; - defaultBillingAddressId?: string; - defaultPaymentMethodId?: string; - defaultShippingAddressId?: string; - /** Format: date-time */ - doubleOptInConfirmDate?: string; - /** Format: date-time */ - doubleOptInEmailSentDate?: string; - doubleOptInRegistration?: boolean; - email?: string; - /** Format: date-time */ - firstLogin?: string; - firstName?: string; - groupId?: string; - guest?: boolean; - hash?: string; - languageId?: string; - /** Format: date-time */ - lastLogin?: string; - lastName?: string; - /** Format: date-time */ - lastOrderDate?: string; - lastPaymentMethodId?: string; - legacyEncoder?: string; - legacyPassword?: string; - newsletter?: boolean; - /** Format: int32 */ - orderCount?: number; - password?: string; - remoteAddress?: string; - salesChannelId?: string; - salutationId?: string; - title?: string; - }; - /** Fallback group if the default customer group is not applicable */ - fallbackCustomerGroup?: { - displayGross?: boolean; - name?: string; - }; - /** Selected payment method */ - paymentMethod?: { - active?: boolean; - availabilityRuleId?: string; - description?: string; - formattedHandlerIdentifier?: string; - handlerIdentifier?: string; - mediaId?: string; - name?: string; - pluginId?: string; - /** Format: int32 */ - position?: number; - }; - /** Information about the current sales channel */ - salesChannel?: { - accessKey?: string; - active?: boolean; - analyticsId?: string; - countryId?: string; - currencyId?: string; - customerGroupId?: string; - footerCategoryId?: string; - hreflangActive?: boolean; - hreflangDefaultDomainId?: string; - languageId?: string; - mailHeaderFooterId?: string; - maintenance?: boolean; - maintenanceIpWhitelist?: string; - name?: string; - /** Format: int32 */ - navigationCategoryDepth?: number; - navigationCategoryId?: string; - paymentMethodId?: string; - serviceCategoryId?: string; - shippingMethodId?: string; - shortName?: string; - typeId?: string; - }; - /** Selected shipping method */ - shippingMethod?: { - active?: boolean; - availabilityRuleId?: string; - deliveryTimeId?: string; - description?: string; - mediaId?: string; - name?: string; - trackingUrl?: string; - }; - /** Currently active tax rules and/or rates */ - taxRules?: { - name?: string; - /** Format: float */ - taxRate?: number; - }[]; - /** Context the user session */ - token?: string; - }; - /** Added since version: 6.0.0.0 */ - SalesChannelDomain: { - /** Format: date-time */ - createdAt: string; - currency?: components['schemas']['Currency']; - currencyId: string; - customFields?: GenericRecord; - hreflangUseOnlyLocale?: boolean; - id?: string; - language?: components['schemas']['Language']; - languageId: string; - salesChannelDefaultHreflang?: components['schemas']['SalesChannel']; - salesChannelId: string; - snippetSetId: string; - /** Format: date-time */ - updatedAt?: string; - url: string; - }; - /** Added since version: 6.0.0.0 */ - SalesChannelType: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Salutation: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - displayName: string; - id?: string; - letterName: string; - salutationKey: string; - translated?: { - displayName?: string; - letterName?: string; - salutationKey?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - SalutationJsonApi: components['schemas']['resource'] & { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - displayName: string; - id?: string; - letterName: string; - salutationKey: string; - translated?: { - displayName?: string; - letterName?: string; - salutationKey?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ScheduledTask: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.7.0 */ - Script: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - SeoUrl: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - foreignKey: string; - id?: string; - isCanonical?: boolean; - isDeleted?: boolean; - isModified?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - isValid?: boolean; - languageId: string; - pathInfo: string; - routeName: string; - salesChannelId?: string; - seoPathInfo: string; - /** Format: date-time */ - updatedAt?: string; - /** Runtime field, cannot be used as part of the criteria. */ - url?: string; - }; - /** Added since version: 6.0.0.0 */ - SeoUrlJsonApi: components['schemas']['resource'] & { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - foreignKey: string; - id?: string; - isCanonical?: boolean; - isDeleted?: boolean; - isModified?: boolean; - /** Runtime field, cannot be used as part of the criteria. */ - isValid?: boolean; - languageId: string; - pathInfo: string; - routeName: string; - salesChannelId?: string; - seoPathInfo: string; - /** Format: date-time */ - updatedAt?: string; - /** Runtime field, cannot be used as part of the criteria. */ - url?: string; - }; - /** Added since version: 6.0.0.0 */ - SeoUrlTemplate: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - isValid?: boolean; - salesChannelId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ShippingMethod: { - active?: boolean; - availabilityRule?: components['schemas']['Rule']; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - deliveryTime?: components['schemas']['DeliveryTime']; - deliveryTimeId: string; - description?: string; - id?: string; - media?: components['schemas']['Media']; - mediaId?: string; - name: string; - /** Format: int64 */ - position?: number; - prices?: components['schemas']['ShippingMethodPrice']; - tags?: components['schemas']['Tag']; - tax?: components['schemas']['Tax']; - taxType: string; - trackingUrl?: string; - translated?: { - deliveryTimeId?: string; - description?: string; - mediaId?: string; - name?: string; - taxType?: string; - trackingUrl?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - ShippingMethodJsonApi: components['schemas']['resource'] & { - active?: boolean; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - deliveryTimeId: string; - description?: string; - id?: string; - mediaId?: string; - name: string; - /** Format: int64 */ - position?: number; - relationships?: { - availabilityRule?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce4ed9f9ee */ - id?: string; - /** @example rule */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/availabilityRule - */ - related?: string; - }; - }; - deliveryTime?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce4e8bf6ea */ - id?: string; - /** @example delivery_time */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/deliveryTime - */ - related?: string; - }; - }; - media?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce4f745547 */ - id?: string; - /** @example media */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/media - */ - related?: string; - }; - }; - prices?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce4f520b9d */ - id?: string; - /** @example shipping_method_price */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/prices - */ - related?: string; - }; - }; - tags?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce500bf752 */ - id?: string; - /** @example tag */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/tags - */ - related?: string; - }; - }; - tax?: { - data?: { - /** @example 0188f8334a4973b68b11e7ce506a4c6d */ - id?: string; - /** @example tax */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /shipping-method/0188f8334a4973b68b11e7ce4e6ade1e/tax - */ - related?: string; - }; - }; - }; - taxType: string; - trackingUrl?: string; - translated?: { - deliveryTimeId?: string; - description?: string; - mediaId?: string; - name?: string; - taxType?: string; - trackingUrl?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - ShippingMethodPageRouteResponse: { - active?: boolean; - availabilityRule?: { - description?: string; - invalid?: boolean; - name?: string; - /** Format: int32 */ - priority?: number; - }; - availabilityRuleId?: string; - deliveryTime?: { - /** Format: int32 */ - max?: number; - /** Format: int32 */ - min?: number; - name?: string; - unit?: string; - }; - deliveryTimeId?: string; - description?: string; - media?: { - alt?: string; - fileExtension?: string; - fileName?: string; - /** Format: int32 */ - fileSize?: number; - mediaFolderId?: string; - mediaTypeRaw?: string; - metaDataRaw?: string; - mimeType?: string; - private?: boolean; - thumbnailsRo?: string; - title?: string; - /** Format: date-time */ - uploadedAt?: string; - url?: string; - userId?: string; - }; - mediaId?: string; - name?: string; - orderDeliveries?: { - orderId?: string; - /** Format: date-time */ - shippingDateEarliest?: string; - /** Format: date-time */ - shippingDateLatest?: string; - shippingMethodId?: string; - shippingOrderAddressId?: string; - stateId?: string; - }[]; - prices?: { - /** Format: int32 */ - calculation?: number; - calculationRuleId?: string; - currencyId?: string; - /** Format: float */ - price?: number; - /** Format: float */ - quantityEnd?: number; - /** Format: float */ - quantityStart?: number; - ruleId?: string; - shippingMethodId?: string; - }[]; - salesChannelDefaultAssignments?: { - accessKey?: string; - active?: boolean; - countryId?: string; - currencyId?: string; - customerGroupId?: string; - footerCategoryId?: string; - hreflangActive?: boolean; - hreflangDefaultDomainId?: string; - languageId?: string; - mailHeaderFooterId?: string; - maintenance?: boolean; - maintenanceIpWhitelist?: string; - name?: string; - /** Format: int32 */ - navigationCategoryDepth?: number; - navigationCategoryId?: string; - paymentMethodId?: string; - serviceCategoryId?: string; - shippingMethodId?: string; - shortName?: string; - typeId?: string; - }[]; - salesChannels?: { - accessKey?: string; - active?: boolean; - countryId?: string; - currencyId?: string; - customerGroupId?: string; - footerCategoryId?: string; - hreflangActive?: boolean; - hreflangDefaultDomainId?: string; - languageId?: string; - mailHeaderFooterId?: string; - maintenance?: boolean; - maintenanceIpWhitelist?: string; - name?: string; - /** Format: int32 */ - navigationCategoryDepth?: number; - navigationCategoryId?: string; - paymentMethodId?: string; - serviceCategoryId?: string; - shippingMethodId?: string; - shortName?: string; - typeId?: string; - }[]; - tags?: { - name?: string; - }[]; - translations?: { - description?: string; - name?: string; - shippingMethodId?: string; - }[]; - }[]; - /** Added since version: 6.0.0.0 */ - ShippingMethodPrice: { - /** Format: int64 */ - calculation?: number; - calculationRuleId?: string; - /** Format: date-time */ - createdAt: string; - currencyPrice?: GenericRecord; - customFields?: GenericRecord; - id?: string; - /** Format: float */ - quantityEnd?: number; - /** Format: float */ - quantityStart?: number; - ruleId?: string; - shippingMethodId: string; - /** Format: date-time */ - updatedAt?: string; - }; - Sitemap: components['schemas']['ArrayStruct'] & { - /** Format: date-time */ - created?: string; - filename?: string; - }; - /** Added since version: 6.0.0.0 */ - Snippet: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - setId: string; - translationKey: string; - /** Format: date-time */ - updatedAt?: string; - value: string; - }; - /** Added since version: 6.0.0.0 */ - SnippetSet: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - iso: string; - name: string; - snippets?: components['schemas']['Snippet']; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - StateMachine: { - /** Format: date-time */ - createdAt: string; - id?: string; - states?: components['schemas']['StateMachineState']; - transitions?: components['schemas']['StateMachineTransition']; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - StateMachineHistory: { - /** Format: date-time */ - createdAt: string; - fromStateMachineState?: components['schemas']['StateMachineState']; - id?: string; - toStateMachineState?: components['schemas']['StateMachineState']; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - StateMachineState: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - technicalName: string; - translated?: { - name?: string; - technicalName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - StateMachineTransition: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - Struct: { - /** Alias which can be used to restrict response fields. For more information see [includes](https://shopware.stoplight.io/docs/store-api/docs/concepts/search-queries.md#includes-apialias). */ - apiAlias?: string; - }; - success: { - data: components['schemas']['data']; - /** To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called "compound documents". */ - included?: components['schemas']['resource'][]; - /** Link members related to the primary data. */ - links?: components['schemas']['links'] & components['schemas']['pagination']; - meta?: components['schemas']['meta']; - }; - SuccessResponse: { - success?: boolean; - }; - /** Added since version: */ - SwagCmsExtensionsBlockRule: { - cmsBlock?: components['schemas']['CmsBlock']; - cmsBlockId: string; - cmsBlockVersionId?: string; - /** Format: date-time */ - createdAt: string; - id?: string; - inverted?: boolean; - /** Format: date-time */ - updatedAt?: string; - visibilityRule?: components['schemas']['Rule']; - visibilityRuleId?: string; - }; - /** Added since version: */ - SwagCmsExtensionsForm: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCmsExtensionsFormGroup: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCmsExtensionsFormGroupField: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCmsExtensionsQuickview: { - active?: boolean; - cmsBlock?: components['schemas']['CmsBlock']; - cmsBlockId?: string; - cmsBlockVersionId?: string; - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCmsExtensionsScrollNavigation: { - active?: boolean; - cmsSection?: components['schemas']['CmsSection']; - cmsSectionId?: string; - cmsSectionVersionId?: string; - /** Format: date-time */ - createdAt: string; - displayName?: string; - id?: string; - translated?: { - cmsSectionId?: string; - cmsSectionVersionId?: string; - displayName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCmsExtensionsScrollNavigationPageSettings: { - active: boolean; - bouncy: boolean; - cmsPage?: components['schemas']['CmsPage']; - cmsPageId?: string; - cmsPageVersionId?: string; - /** Format: date-time */ - createdAt: string; - /** Format: int64 */ - duration: number; - easing: string; - /** Format: int64 */ - easingDegree: number; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplate: { - active?: boolean; - configurations?: components['schemas']['SwagCustomizedProductsTemplateConfiguration']; - confirmInput?: boolean; - /** Format: date-time */ - createdAt: string; - decisionTree?: GenericRecord; - description?: string; - displayName: string; - exclusions?: components['schemas']['SwagCustomizedProductsTemplateExclusion']; - id?: string; - internalName: string; - media?: components['schemas']['Media']; - mediaId?: string; - options?: components['schemas']['SwagCustomizedProductsTemplateOption']; - optionsAutoCollapse?: boolean; - parentVersionId?: string; - products?: components['schemas']['Product']; - stepByStep?: boolean; - translated?: { - description?: string; - displayName?: string; - internalName?: string; - mediaId?: string; - parentVersionId?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateConfiguration: { - configuration: GenericRecord; - /** Format: date-time */ - createdAt: string; - hash: string; - id?: string; - template?: components['schemas']['SwagCustomizedProductsTemplate']; - templateConfigurationShares?: components['schemas']['SwagCustomizedProductsTemplateConfigurationShare']; - templateId: string; - templateVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateConfigurationJsonApi: components['schemas']['resource'] & { - configuration: GenericRecord; - /** Format: date-time */ - createdAt: string; - hash: string; - id?: string; - relationships?: { - template?: { - data?: { - /** @example 0188f8334a4b73b8a052702133a9a162 */ - id?: string; - /** @example swag_customized_products_template */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-configuration/0188f8334a4b73b8a052702132e04837/template - */ - related?: string; - }; - }; - templateConfigurationShares?: { - data?: { - /** @example 0188f8334a4b73b8a05270213400704d */ - id?: string; - /** @example swag_customized_products_template_configuration_share */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-configuration/0188f8334a4b73b8a052702132e04837/templateConfigurationShares - */ - related?: string; - }; - }; - }; - templateId: string; - templateVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateConfigurationShare: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateExclusion: { - conditions?: components['schemas']['SwagCustomizedProductsTemplateExclusionCondition']; - /** Format: date-time */ - createdAt: string; - id?: string; - name: string; - template?: components['schemas']['SwagCustomizedProductsTemplate']; - templateId: string; - templateVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateExclusionCondition: { - /** Format: date-time */ - createdAt: string; - id?: string; - templateExclusion?: components['schemas']['SwagCustomizedProductsTemplateExclusion']; - templateExclusionId: string; - templateExclusionOperator?: components['schemas']['SwagCustomizedProductsTemplateExclusionOperator']; - templateExclusionOperatorId: string; - templateExclusionVersionId?: string; - templateOption?: components['schemas']['SwagCustomizedProductsTemplateOption']; - templateOptionId: string; - templateOptionValues?: components['schemas']['SwagCustomizedProductsTemplateOptionValue']; - templateOptionVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateExclusionOperator: { - /** Format: date-time */ - createdAt: string; - id?: string; - label: string; - operator: string; - templateExclusionConditions?: components['schemas']['SwagCustomizedProductsTemplateExclusionCondition']; - templateOptionType: string; - translated?: { - label?: string; - operator?: string; - templateOptionType?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateJsonApi: components['schemas']['resource'] & { - active?: boolean; - confirmInput?: boolean; - /** Format: date-time */ - createdAt: string; - decisionTree?: GenericRecord; - description?: string; - displayName: string; - id?: string; - internalName: string; - mediaId?: string; - optionsAutoCollapse?: boolean; - parentVersionId?: string; - relationships?: { - configurations?: { - data?: { - /** @example 0188f8334a4b73b8a052702132b1488f */ - id?: string; - /** @example swag_customized_products_template_configuration */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template/0188f8334a4b73b8a052702130b55d12/configurations - */ - related?: string; - }; - }; - exclusions?: { - data?: { - /** @example 0188f8334a4b73b8a052702132a974ef */ - id?: string; - /** @example swag_customized_products_template_exclusion */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template/0188f8334a4b73b8a052702130b55d12/exclusions - */ - related?: string; - }; - }; - media?: { - data?: { - /** @example 0188f8334a4b73b8a052702130f74665 */ - id?: string; - /** @example media */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template/0188f8334a4b73b8a052702130b55d12/media - */ - related?: string; - }; - }; - options?: { - data?: { - /** @example 0188f8334a4b73b8a052702131e12911 */ - id?: string; - /** @example swag_customized_products_template_option */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template/0188f8334a4b73b8a052702130b55d12/options - */ - related?: string; - }; - }; - products?: { - data?: { - /** @example 0188f8334a4b73b8a052702131ef8519 */ - id?: string; - /** @example product */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template/0188f8334a4b73b8a052702130b55d12/products - */ - related?: string; - }; - }; - }; - stepByStep?: boolean; - translated?: { - description?: string; - displayName?: string; - internalName?: string; - mediaId?: string; - parentVersionId?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOption: { - advancedSurcharge?: boolean; - calculatedPrice?: GenericRecord; - /** Format: date-time */ - createdAt: string; - description?: string; - displayName: string; - id?: string; - itemNumber?: string; - oneTimeSurcharge?: boolean; - /** Format: float */ - percentageSurcharge?: number; - placeholder?: string; - /** Format: int64 */ - position?: number; - price?: GenericRecord; - prices?: components['schemas']['SwagCustomizedProductsTemplateOptionPrice']; - relativeSurcharge?: boolean; - required?: boolean; - tax?: components['schemas']['Tax']; - taxId?: string; - template?: components['schemas']['SwagCustomizedProductsTemplate']; - templateExclusionConditions?: components['schemas']['SwagCustomizedProductsTemplateExclusionCondition']; - templateId: string; - templateVersionId?: string; - translated?: { - description?: string; - displayName?: string; - itemNumber?: string; - placeholder?: string; - taxId?: string; - templateId?: string; - templateVersionId?: string; - type?: string; - }; - type: string; - typeProperties?: GenericRecord; - /** Format: date-time */ - updatedAt?: string; - values?: components['schemas']['SwagCustomizedProductsTemplateOptionValue']; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOptionJsonApi: components['schemas']['resource'] & { - advancedSurcharge?: boolean; - calculatedPrice?: GenericRecord; - /** Format: date-time */ - createdAt: string; - description?: string; - displayName: string; - id?: string; - itemNumber?: string; - oneTimeSurcharge?: boolean; - /** Format: float */ - percentageSurcharge?: number; - placeholder?: string; - /** Format: int64 */ - position?: number; - price?: GenericRecord; - relationships?: { - prices?: { - data?: { - /** @example 0188f8334a4c7391b3c2465a968e2341 */ - id?: string; - /** @example swag_customized_products_template_option_price */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option/0188f8334a4c7391b3c2465a9651c8e9/prices - */ - related?: string; - }; - }; - tax?: { - data?: { - /** @example 0188f8334a4d701badec9fc5347f84d1 */ - id?: string; - /** @example tax */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option/0188f8334a4c7391b3c2465a9651c8e9/tax - */ - related?: string; - }; - }; - template?: { - data?: { - /** @example 0188f8334a4d701badec9fc533c5f8dc */ - id?: string; - /** @example swag_customized_products_template */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option/0188f8334a4c7391b3c2465a9651c8e9/template - */ - related?: string; - }; - }; - templateExclusionConditions?: { - data?: { - /** @example 0188f8334a4d701badec9fc5334d96ab */ - id?: string; - /** @example swag_customized_products_template_exclusion_condition */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option/0188f8334a4c7391b3c2465a9651c8e9/templateExclusionConditions - */ - related?: string; - }; - }; - values?: { - data?: { - /** @example 0188f8334a4d701badec9fc533327b23 */ - id?: string; - /** @example swag_customized_products_template_option_value */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option/0188f8334a4c7391b3c2465a9651c8e9/values - */ - related?: string; - }; - }; - }; - relativeSurcharge?: boolean; - required?: boolean; - taxId?: string; - templateId: string; - templateVersionId?: string; - translated?: { - description?: string; - displayName?: string; - itemNumber?: string; - placeholder?: string; - taxId?: string; - templateId?: string; - templateVersionId?: string; - type?: string; - }; - type: string; - typeProperties?: GenericRecord; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOptionPrice: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: float */ - percentageSurcharge?: number; - price?: GenericRecord; - rule?: components['schemas']['Rule']; - ruleId?: string; - templateOption?: components['schemas']['SwagCustomizedProductsTemplateOption']; - templateOptionId: string; - templateOptionVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOptionValue: { - advancedSurcharge?: boolean; - /** Format: date-time */ - createdAt: string; - default?: boolean; - displayName: string; - id?: string; - itemNumber?: string; - oneTimeSurcharge?: boolean; - /** Format: float */ - percentageSurcharge?: number; - /** Format: int64 */ - position: number; - price?: GenericRecord; - prices?: components['schemas']['SwagCustomizedProductsTemplateOptionValuePrice']; - relativeSurcharge?: boolean; - tax?: components['schemas']['Tax']; - taxId?: string; - templateExclusionConditions?: components['schemas']['SwagCustomizedProductsTemplateExclusionCondition']; - templateOption?: components['schemas']['SwagCustomizedProductsTemplateOption']; - templateOptionId: string; - templateOptionVersionId?: string; - translated?: { - displayName?: string; - itemNumber?: string; - taxId?: string; - templateOptionId?: string; - templateOptionVersionId?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - value?: GenericRecord; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOptionValueJsonApi: components['schemas']['resource'] & { - advancedSurcharge?: boolean; - /** Format: date-time */ - createdAt: string; - default?: boolean; - displayName: string; - id?: string; - itemNumber?: string; - oneTimeSurcharge?: boolean; - /** Format: float */ - percentageSurcharge?: number; - /** Format: int64 */ - position: number; - price?: GenericRecord; - relationships?: { - prices?: { - data?: { - /** @example 0188f8334a4d701badec9fc53843050a */ - id?: string; - /** @example swag_customized_products_template_option_value_price */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option-value/0188f8334a4d701badec9fc5375ebce7/prices - */ - related?: string; - }; - }; - tax?: { - data?: { - /** @example 0188f8334a4d701badec9fc538b6a4b7 */ - id?: string; - /** @example tax */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option-value/0188f8334a4d701badec9fc5375ebce7/tax - */ - related?: string; - }; - }; - templateExclusionConditions?: { - data?: { - /** @example 0188f8334a4d701badec9fc5390730a1 */ - id?: string; - /** @example swag_customized_products_template_exclusion_condition */ - type?: string; - }[]; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option-value/0188f8334a4d701badec9fc5375ebce7/templateExclusionConditions - */ - related?: string; - }; - }; - templateOption?: { - data?: { - /** @example 0188f8334a4d701badec9fc538670459 */ - id?: string; - /** @example swag_customized_products_template_option */ - type?: string; - }; - links?: { - /** - * Format: uri-reference - * @example /swag-customized-products-template-option-value/0188f8334a4d701badec9fc5375ebce7/templateOption - */ - related?: string; - }; - }; - }; - relativeSurcharge?: boolean; - taxId?: string; - templateOptionId: string; - templateOptionVersionId?: string; - translated?: { - displayName?: string; - itemNumber?: string; - taxId?: string; - templateOptionId?: string; - templateOptionVersionId?: string; - versionId?: string; - }; - /** Format: date-time */ - updatedAt?: string; - value?: GenericRecord; - versionId?: string; - }; - /** Added since version: */ - SwagCustomizedProductsTemplateOptionValuePrice: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: float */ - percentageSurcharge?: number; - price?: GenericRecord; - rule?: components['schemas']['Rule']; - ruleId?: string; - templateOptionValue?: components['schemas']['SwagCustomizedProductsTemplateOptionValue']; - templateOptionValueId: string; - templateOptionValueVersionId?: string; - /** Format: date-time */ - updatedAt?: string; - versionId?: string; - }; - /** Added since version: */ - SwagDelayAction: { - /** Format: date-time */ - createdAt: string; - customerId?: string; - id?: string; - orderId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagDynamicAccessLandingPageRule: { - id?: string; - landingPage?: components['schemas']['LandingPage']; - landingPageId: string; - landingPageVersionId?: string; - rule?: components['schemas']['Rule']; - ruleId: string; - }; - /** Added since version: */ - SwagDynamicAccessProductRule: { - id?: string; - product?: components['schemas']['Product']; - productId: string; - productVersionId?: string; - rule?: components['schemas']['Rule']; - ruleId: string; - }; - /** Added since version: */ - SwagLanguagePackLanguage: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationConnection: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationData: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationGeneralSetting: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationLogging: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationMapping: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationMediaFile: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagMigrationRun: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagPaypalPosSalesChannel: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagPaypalPosSalesChannelRun: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagPaypalPosSalesChannelRunLog: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagSocialShoppingCustomer: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagSocialShoppingOrder: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagSocialShoppingProductError: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - SwagSocialShoppingSalesChannel: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - SystemConfig: { - configurationKey: string; - configurationValue: { - _value?: GenericRecord; - }; - /** Format: date-time */ - createdAt: string; - id?: string; - salesChannel?: components['schemas']['SalesChannel']; - salesChannelId?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Tag: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Tax: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - /** - * Format: int64 - * Added since version: 6.4.0.0. - */ - position: number; - /** Format: float */ - taxRate: number; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.5.0.0 */ - TaxProvider: { - active?: boolean; - appId?: string; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - /** Format: int64 */ - priority: number; - processUrl?: string; - translated?: { - appId?: string; - name?: string; - processUrl?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.1.0.0 */ - TaxRule: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.1.0.0 */ - TaxRuleType: { - /** Format: date-time */ - createdAt: string; - id?: string; - translated?: Record; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Theme: { - active: boolean; - author: string; - baseConfig?: GenericRecord; - configValues?: GenericRecord; - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - description?: string; - helpTexts?: GenericRecord; - id?: string; - labels?: GenericRecord; - media?: components['schemas']['Media']; - name: string; - parentThemeId?: string; - previewMediaId?: string; - technicalName?: string; - translated?: { - author?: string; - description?: string; - name?: string; - parentThemeId?: string; - previewMediaId?: string; - technicalName?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - Unit: { - /** Format: date-time */ - createdAt: string; - customFields?: GenericRecord; - id?: string; - name: string; - shortCode: string; - translated?: { - name?: string; - shortCode?: string; - }; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - User: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - UserAccessKey: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.5.0 */ - UserConfig: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.0.0.0 */ - UserRecovery: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - Warehouse: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: */ - WarehouseGroup: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.3.1.0 */ - Webhook: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - /** Added since version: 6.4.1.0 */ - WebhookEventLog: { - /** Format: date-time */ - createdAt: string; - id?: string; - /** Format: date-time */ - updatedAt?: string; - }; - WishlistLoadRouteResponse: { - products?: components['schemas']['ProductListingResult'][]; - wishlist?: { - customerId?: string; - salesChannelId?: string; - }; - }; - }; - responses: { - /** No Content */ - 204: never; - /** Bad Request */ - 400: { - content: { - 'application/json': components['schemas']['failure']; - 'application/vnd.api+json': components['schemas']['failure']; - }; - }; - /** Unauthorized */ - 401: { - content: { - 'application/json': components['schemas']['failure']; - 'application/vnd.api+json': components['schemas']['failure']; - }; - }; - /** Forbidden */ - 403: { - content: { - 'application/json': components['schemas']['failure']; - 'application/vnd.api+json': components['schemas']['failure']; - }; - }; - /** Not Found */ - 404: { - content: { - 'application/json': components['schemas']['failure']; - 'application/vnd.api+json': components['schemas']['failure']; - }; - }; - }; - parameters: { - /** Accepted response content types */ - accept: string; - /** Content type of the request */ - contentType: string; - }; - requestBodies: never; - headers: never; - pathItems: never; -}; - -export type external = Record; - -export type operations = { - /** - * Create a new address for a customer - * Creates a new address for a customer. - */ - createCustomerAddress: { - requestBody?: { - content: { - 'application/json': components['schemas']['CustomerAddress']; - }; - }; - responses: { - 200: { - content: { - 'application/json': components['schemas']['CustomerAddress']; - }; - }; - }; - }; - /** - * Delete an address of a customer - * Delete an address of customer. - * - * Only addresses which are not set as default addresses for shipping or billing can be deleted. You can check the current default addresses of your customer using the profile information endpoint and change them using the default address endpoint. - * - * **A customer must have at least one address (which can be used for shipping and billing).** - * - * An automatic fallback is not applied. - */ - deleteCustomerAddress: { - parameters: { - path: { - /** ID of the address to be deleted. */ - addressId: string; - }; - }; - responses: { - /** No Content response, when the address has been deleted */ - 204: never; - /** Response containing a list of errors, most likely due to the address being in use */ - 400: never; - }; - }; - /** - * Modify an address of a customer - * Modifies an existing address of a customer. - */ - updateCustomerAddress: { - parameters: { - path: { - /** Address ID */ - addressId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['CustomerAddress']; - }; - }; - responses: { - 200: { - content: { - 'application/json': components['schemas']['CustomerAddress']; - }; - }; - }; - }; - /** - * Change a customer's default billing address - * Updates the default (preselected) billing addresses of a customer. - */ - defaultBillingAddress: { - parameters: { - path: { - /** Address ID */ - addressId: string; - }; - }; - responses: { - 200: never; - }; - }; - /** - * Change a customer's default shipping address - * Updates the default (preselected) shipping addresses of a customer. - */ - defaultShippingAddress: { - parameters: { - path: { - /** Address ID */ - addressId: string; - }; - }; - responses: { - 200: never; - }; - }; - /** - * Change the customer's email address - * Changes a customer's email address to a new email address, using their current password as a validation. - */ - changeEmail: { - requestBody: { - content: { - 'application/json': { - /** New email address. Has to be unique amongst all customers */ - email: string; - /** Confirmation of the new email address. */ - emailConfirmation: string; - /** Customer's current password */ - password: string; - }; - }; - }; - responses: { - /** Returns a success response indicating a successful update */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Change the customer's language. - * Changes the language of the logged in customer - */ - changeLanguage: { - requestBody: { - content: { - 'application/json': { - /** New languageId */ - language?: string; - }; - }; - }; - responses: { - /** Returns a success response indicating a successful update */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Change the customer's password - * Changes a customer's password using their current password as a validation. - */ - changePassword: { - requestBody: { - content: { - 'application/json': { - /** New Password for the customer */ - newPassword: string; - /** Confirmation of the new password */ - newPasswordConfirm: string; - /** Current password of the customer */ - password: string; - }; - }; - }; - responses: { - /** Returns a success response indicating a successful update. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Change the customer's default payment method - * Changes a customer's default (preselected) payment method. - */ - changePaymentMethod: { - parameters: { - path: { - /** Identifier of the desired default payment method */ - paymentMethodId: string; - }; - }; - responses: { - /** Returns a success response indicating a successful update. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Change the customer's information - * Make changes to a customer's account, like changing their name, salutation or title. - */ - changeProfile: { - requestBody: { - content: { - 'application/json': { - /** Birthday day */ - birthdayDay?: number; - /** Birthday month */ - birthdayMonth?: number; - /** Birthday year */ - birthdayYear?: number; - /** Company of the customer. Only required when `accountType` is `business`. */ - company?: string; - /** Customer first name. Value will be reused for shipping and billing address if not provided explicitly. */ - firstName: string; - /** Customer last name. Value will be reused for shipping and billing address if not provided explicitly. */ - lastName: string; - /** Id of the salutation for the customer account. Fetch options using `salutation` endpoint. */ - salutationId: string; - /** (Academic) title of the customer */ - title?: string; - }; - }; - }; - responses: { - /** Returns a success response indicating a successful update */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Get information about current customer - * Returns information about the current customer. - */ - readCustomer: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Returns the logged in customer, also for guest sessions. Check for the value of `guest` field to see whether the customer is a guest. */ - 200: { - content: { - 'application/json': components['schemas']['Customer']; - }; - }; - }; - }; - /** - * Delete the customer's profile - * Deletes a customer profile along with their addresses, wishlists and associated data. Created orders and their payment/shipping information (addresses) and reviews are not deleted. - */ - deleteCustomer: { - responses: { - /** Returns a no content response indicating a successful removal of the customer profile */ - 204: never; - }; - }; - /** - * Checks if the customer recovery entry for a given hash is expired. - * This can be used to validate a provided hash has a valid and not expired customer recovery hash. - */ - getCustomerRecoveryIsExpired: { - requestBody: { - content: { - 'application/json': { - /** Parameter from the link in the confirmation mail sent in Step 1 */ - hash: string; - }; - }; - }; - responses: { - /** Returns a CustomerRecoveryIsExpiredResponse that indicates if the hash is expired or not. */ - 200: { - content: { - 'application/json': components['schemas']['ArrayStruct']; - }; - }; - }; - }; - /** - * Fetch addresses of a customer - * Lists all addresses of the current customer and allows filtering them based on a criteria. - */ - listAddress: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - 200: { - content: { - 'application/json': components['schemas']['CustomerAddress'][]; - }; - }; - }; - }; - /** - * Log in a customer - * Logs in customers given their credentials. - */ - loginCustomer: { - requestBody: { - content: { - 'application/json': { - /** Password */ - password: string; - /** Email */ - username: string; - }; - }; - }; - responses: { - /** A successful login returns a context token which is associated with the logged in user. Use that as your `sw-context-token` header for subsequent requests. */ - 200: { - content: { - 'application/json': components['schemas']['ContextTokenResponse']; - }; - }; - /** If credentials are incorrect an error is returned */ - 401: { - content: { - 'application/json': components['schemas']['failure']; - }; - }; - }; - }; - /** - * Log out a customer - * Logs out a customer. - */ - logoutCustomer: { - responses: { - /** A successful logout returns a context token for the anonymous user. Use that as your `sw-context-token` header for subsequent requests. */ - 200: { - content: { - 'application/json': components['schemas']['ContextTokenResponse']; - }; - }; - 403: components['responses']['403']; - }; - }; - /** - * Fetch newsletter recipients - * Perform a filtered search for newsletter recipients. - */ - readNewsletterRecipient: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - 200: { - content: { - 'application/json': unknown; - }; - }; - }; - }; - /** - * Send a password recovery mail - * This operation is Step 1 of the password reset flow. Make sure to implement Step 2 "Reset password with recovery credentials" in order to allow for the complete flow in your application. Sends a recovery mail containing a link with credentials that allows a customer to reset their password. - */ - sendRecoveryMail: { - requestBody: { - content: { - 'application/json': { - /** E-Mail address to identify the customer */ - email: string; - /** URL of the storefront to use for the generated reset link. It has to be a domain that is configured in the sales channel domain settings. */ - storefrontUrl: string; - }; - }; - }; - responses: { - /** - * If email corresponds to an existing customer, a mail will be sent out to that customer containing a link assembled using the following schema: - * - * Returns a success indicating a successful initialisation of the reset flow. - */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Reset a password with recovery credentials - * This operation is Step 2 of the password reset flow. It is required to conduct Step 1 "Send a password recovery mail" in order to obtain the required credentials for this step.Resets a customer's password using credentials from a password recovery mail as a validation. - */ - recoveryPassword: { - requestBody: { - content: { - 'application/json': { - /** Parameter from the link in the confirmation mail sent in Step 1 */ - hash: string; - /** New password for the customer */ - newPassword: string; - /** Confirmation of the new password */ - newPasswordConfirm: string; - }; - }; - }; - responses: { - /** Returns a success response indicating a successful update. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Register a customer - * Registers a customer. Used both for normal customers and guest customers.See the Guide "Register a customer" for more information on customer registration. - */ - register: { - requestBody: { - content: { - 'application/json': { - /** Flag indicating accepted data protection */ - acceptedDataProtection: boolean; - /** - * Account type of the customer which can be either `private` or `business`. - * @default private - */ - accountType?: string; - /** Field can be used to store an affiliate tracking code */ - affiliateCode?: string; - billingAddress: components['schemas']['CustomerAddress']; - /** Birthday day */ - birthdayDay?: number; - /** Birthday month */ - birthdayMonth?: number; - /** Birthday year */ - birthdayYear?: number; - /** Field can be used to store a campaign tracking code */ - campaignCode?: string; - /** Email of the customer. Has to be unique, unless `guest` is `true` */ - email: string; - /** Customer first name. Value will be reused for shipping and billing address if not provided explicitly. */ - firstName: string; - /** - * If set, will create a guest customer. Guest customers can re-use an email address and don't need a password. - * @default false - */ - guest?: boolean; - /** Customer last name. Value will be reused for shipping and billing address if not provided explicitly. */ - lastName: string; - /** Password for the customer. Required, unless `guest` is `true` */ - password: string; - /** Id of the salutation for the customer account. Fetch options using `salutation` endpoint. */ - salutationId: string; - shippingAddress?: components['schemas']['CustomerAddress']; - /** URL of the storefront for that registration. Used in confirmation emails. Has to be one of the configured domains of the sales channel. */ - storefrontUrl: string; - /** (Academic) title of the customer */ - title?: string; - }; - }; - }; - responses: { - /** Success */ - 200: { - content: { - 'application/json': components['schemas']['Customer']; - }; - }; - }; - }; - /** - * Confirm a customer registration - * Confirms a customer registration when double opt-in is activated. - * - * Learn more about double opt-in registration in our guide "Register a customer". - */ - registerConfirm: { - requestBody: { - content: { - 'application/json': { - /** Email hash from the email received */ - em: string; - /** Hash from the email received */ - hash: string; - }; - }; - }; - responses: { - /** Returns the logged in customer. The customer is automatically logged in with the `sw-context-token` header provided, which can be reused for subsequent requests. */ - 200: never; - /** No hash provided */ - 404: never; - /** The customer has already been confirmed */ - 412: never; - }; - }; - /** - * Fetch a list of categories - * Perform a filtered search for categories. - */ - readCategoryList: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing categories. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Category'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** - * Fetch a single category - * This endpoint returns information about the category, as well as a fully resolved (hydrated with mapping values) CMS page, if one is assigned to the category. You can pass slots which should be resolved exclusively. - */ - readCategory: { - parameters: { - query?: { - /** Resolves only the given slot identifiers. The identifiers have to be seperated by a '|' character */ - slots?: string; - }; - path: { - /** Identifier of the category to be fetched */ - navigationId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria'] & - components['schemas']['ProductListingCriteria']; - }; - }; - responses: { - /** The loaded category with cms page */ - 200: { - content: { - 'application/json': components['schemas']['Category']; - }; - }; - }; - }; - /** - * Fetch or create a cart - * Used to fetch the current cart or for creating a new one. - */ - readCart: { - parameters: { - query?: { - /** The name of the new cart. This parameter will only be used when creating a new cart. */ - name?: string; - }; - }; - responses: { - /** Cart */ - 200: { - content: { - 'application/json': components['schemas']['Cart']; - }; - }; - }; - }; - /** - * Delete a cart - * This route deletes the cart of the customer. - */ - deleteCart: { - responses: { - /** Successfully deleted the cart */ - 204: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Add items to the cart - * This route adds items to the cart. An item can be a product or promotion for example. They are referenced by the `referencedId`-parameter. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#adding-new-items-to-the-cart) - */ - addLineItem: { - requestBody?: { - content: { - 'application/json': components['schemas']['CartItems']; - }; - }; - responses: { - /** The updated cart. */ - 200: { - content: { - 'application/json': components['schemas']['Cart']; - }; - }; - }; - }; - /** - * Remove items from the cart - * This route removes items from the cart and recalculates it. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#deleting-items-in-the-cart) - */ - removeLineItem: { - parameters: { - query: { - /** A list of product identifiers. */ - ids: string[]; - }; - }; - responses: { - /** The updated cart. */ - 200: { - content: { - 'application/json': components['schemas']['Cart']; - }; - }; - }; - }; - /** - * Update items in the cart - * This route updates items in the cart. A typical example is updating the quantity of an item. - * - * Example: [Working with the cart - Guide](https://developer.shopware.com/docs/guides/integrations-api/store-api-guide/work-with-the-cart#updating-items-in-the-cart) - */ - updateLineItem: { - requestBody?: { - content: { - 'application/json': components['schemas']['CartItems']; - }; - }; - responses: { - /** The updated cart. */ - 200: { - content: { - 'application/json': components['schemas']['Cart']; - }; - }; - }; - }; - /** - * Create an order from a cart - * Creates a new order from the current cart and deletes the cart. - * - * If you are using the [prepared payment flow](https://developer.shopware.com/docs/concepts/commerce/checkout-concept/payments#2.1-prepare-payment-optional), this endpoint also receives additional transaction details. The exact name of the parameters depends on the implementation of the corresponding *payment handler*. - */ - createOrder: { - /** Contains additional metadata which is stored together with the order. It can also contain payment transaction details. */ - requestBody?: { - content: { - 'application/json': { - /** The affiliate code can be used to track which referrer the customer came through. An example could be `Price-comparison-company-XY`. */ - affiliateCode?: string; - /** The campaign code is used to track which action the customer came from. An example could be `Summer-Deals` */ - campaignCode?: string; - /** Adds a comment from the customer to the order. */ - customerComment?: string; - }; - }; - }; - responses: { - /** Order */ - 200: { - content: { - 'application/json': components['schemas']['Order']; - }; - }; - }; - }; - /** - * Fetch and resolve a CMS page - * Loads a content management page by its identifier and resolve the slot data. This could be media files, product listing and so on. - * - * **Important notice** - * - * The criteria passed with this route also affects the listing, if there is one within the cms page. - */ - readCms: { - parameters: { - path: { - /** Identifier of the CMS page to be resolved */ - id: string; - }; - }; - requestBody?: { - content: { - 'application/json': { - /** Resolves only the given slot identifiers. The identifiers have to be seperated by a `|` character. */ - slots?: string; - } & components['schemas']['ProductListingCriteria']; - }; - }; - responses: { - /** The loaded cms page */ - 200: { - content: { - 'application/json': components['schemas']['CmsPage']; - }; - }; - 404: components['responses']['404']; - }; - }; - /** - * Submit a contact form message - * Used for submitting contact forms. Be aware that there can be more required fields, depending on the system settings. - */ - sendContactMail: { - requestBody: { - content: { - 'application/json': { - /** Type of the content management page */ - cmsPageType?: string; - /** The message of the contact form */ - comment: string; - /** Email address */ - email: string; - /** Entity name for slot config */ - entityName?: string; - /** Firstname. This field may be required depending on the system settings. */ - firstName?: string; - /** Lastname. This field may be required depending on the system settings. */ - lastName?: string; - /** - * Identifier of the navigation page. Can be used to override the configuration. - * Take a look at the settings of a category containing a concact form in the administration. - */ - navigationId?: string; - /** Phone. This field may be required depending on the system settings. */ - phone?: string; - /** Identifier of the salutation. Use `/api/salutation` endpoint to fetch possible values. */ - salutationId: string; - /** Identifier of the cms element */ - slotId?: string; - /** The subject of the contact form. */ - subject: string; - }; - }; - }; - responses: { - /** Message sent successful. */ - 200: never; - }; - }; - /** - * Fetch the current context - * Fetches the current context. This includes for example the `customerGroup`, `currency`, `taxRules` and many more. - */ - readContext: { - responses: { - /** Returns the current context. */ - 200: { - content: { - 'application/json': components['schemas']['SalesChannelContext']; - }; - }; - }; - }; - /** - * Modify the current context - * Used for switching the context. A typical example would be changing the language or changing the currency. - */ - updateContext: { - requestBody: { - content: { - 'application/json': { - /** Billing Address */ - billingAddressId?: string; - /** Country */ - countryId?: string; - /** Country State */ - countryStateId?: string; - /** Currency */ - currencyId?: string; - /** Language */ - languageId?: string; - /** Payment Method */ - paymentMethodId?: string; - /** Shipping Address */ - shippingAddressId?: string; - /** Shipping Method */ - shippingMethodId?: string; - }; - }; - }; - responses: { - /** Returns the context token. Use that as your `sw-context-token` header for subsequent requests. Redirect if getRedirectUrl is set. */ - 200: { - content: { - 'application/json': components['schemas']['ContextTokenResponse']; - }; - }; - }; - }; - /** - * Fetch countries - * Perform a filtered search for countries - */ - readCountry: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing countries. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Country'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** - * Fetch the states of a country - * Perform a filtered search the states for a country - */ - readCountryState: { - parameters: { - path: { - countryId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing countries. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['CountryState'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** - * Fetch currencies - * Perform a filtered search for currencies. - */ - readCurrency: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing currencies. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Currency'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** Fetch registration settings for customer group */ - getCustomerGroupRegistrationInfo: { - parameters: { - path: { - /** Customer group id */ - customerGroupId: string; - }; - }; - responses: { - /** Returns the customer group including registration settings. */ - 200: { - content: { - 'application/json': components['schemas']['CustomerGroup']; - }; - }; - }; - }; - /** - * Fetch a wishlist - * Fetch a customer's wishlist. Products on the wishlist can be filtered using a criteria object. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - readCustomerWishlist: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - 200: { - content: { - 'application/json': components['schemas']['WishlistLoadRouteResponse']; - }; - }; - }; - }; - /** - * Add a product to a wishlist - * Adds a product to a customers wishlist. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - addProductOnWishlist: { - parameters: { - path: { - /** Identifier of the product to be added. */ - productId: string; - }; - }; - responses: { - /** Returns a success response. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Remove a product from a wishlist - * Removes a product from a customer's wishlist. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * The wishlist feature has to be activated. - */ - deleteProductOnWishlist: { - parameters: { - path: { - /** The identifier of the product to be removed from the wishlist. */ - productId: string; - }; - }; - responses: { - /** Returns a success response indicating a successful removal. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - /** The removal of the product failed. Probably because the product could not be found on the wishlist. */ - 404: { - content: { - 'application/json': components['schemas']['failure']; - }; - }; - }; - }; - /** - * Create a wishlist for a customer - * Create a new wishlist for a logged in customer or extend the existing wishlist given a set of products. - * - * **Important constraints** - * - * * Anonymous (not logged-in) customers can not have wishlists. - * * A customer can only have a single wishlist. - * * The wishlist feature has to be activated. - */ - mergeProductOnWishlist: { - requestBody: { - content: { - 'application/json': { - /** List product id */ - productIds?: string[]; - }; - }; - }; - responses: { - /** Returns a success response. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Download generated document - * Returns blob file of a generated document to download. - */ - download: { - parameters: { - path: { - documentId: string; - deepLinkCode: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Returns the document information and blob to download. */ - 200: { - content: { - 'application/json': components['schemas']['Document']; - }; - }; - }; - }; - /** - * Initiate a payment for an order - * This generic endpoint is should be called to initiate a payment flow after an order has been created. The details of the payment flow can differ depending on the payment integration and might require calling additional operations or the setup of webhooks. - * - * The endpoint internally calls the payment handler of the payment method currently set for the order. - */ - handlePaymentMethod: { - requestBody: { - content: { - 'application/json': { - /** URL to which the client should be redirected after erroneous payment */ - errorUrl?: string; - /** URL to which the client should be redirected after successful payment */ - finishUrl?: string; - /** Identifier of an order */ - orderId: string; - }; - }; - }; - responses: { - /** Redirect to external payment provider */ - 200: never; - }; - }; - /** - * Fetch a landing page with the resolved CMS page - * Loads a landing page by its identifier and resolves the CMS page. - * - * **Important notice** - * - * The criteria passed with this route also affects the listing, if there is one within the cms page. - */ - readLandingPage: { - parameters: { - path: { - /** Identifier of the landing page. */ - landingPageId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria'] & - ({ - /** Resolves only the given slot identifiers. The identifiers have to be seperated by a `|` character. */ - slots?: string; - } & components['schemas']['ProductListingCriteria']); - }; - }; - responses: { - /** The loaded landing page with cms page */ - 200: { - content: { - 'application/json': components['schemas']['LandingPage']; - }; - }; - 404: components['responses']['404']; - }; - }; - /** - * Fetch languages - * Perform a filtered search for languages. - */ - readLanguages: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing languages. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Language'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** - * Fetch a navigation menu - * This endpoint returns categories that can be used as a page navigation. You can either return them as a tree or as a flat list. You can also control the depth of the tree. - * - * Instead of passing uuids, you can also use one of the following aliases for the activeId and rootId parameters to get the respective navigations of your sales channel. - * - * * main-navigation - * * service-navigation - * * footer-navigation - */ - readNavigation: { - parameters: { - header?: { - /** Instructs Shopware to try and resolve SEO URLs for the given navigation item */ - 'sw-include-seo-urls'?: boolean; - }; - path: { - /** Identifier of the active category in the navigation tree (if not used, just set to the same as rootId). */ - activeId: string; - /** Identifier of the root category for your desired navigation tree. You can use it to fetch sub-trees of your navigation tree. */ - rootId: string; - }; - }; - requestBody: { - content: { - 'application/json': components['schemas']['Criteria'] & { - /** Return the categories as a tree or as a flat list. */ - buildTree?: unknown; - /** Determines the depth of fetched navigation levels. */ - depth?: unknown; - }; - }; - }; - responses: { - /** All available navigations */ - 200: { - content: { - 'application/json': components['schemas']['NavigationRouteResponse']; - }; - }; - }; - }; - /** - * Confirm a newsletter registration - * You have to use the hash from the link sent out via email to confirm the user registration. - */ - confirmNewsletter: { - requestBody: { - content: { - 'application/json': { - /** Email hash parameter from the link in the confirmation mail */ - em: string; - /** Hash parameter from link the in the confirmation mail */ - hash: string; - }; - }; - }; - responses: { - /** The newsletter confirmation was successful. */ - 200: never; - }; - }; - /** - * Create or remove a newsletter subscription - * This route is used to create/remove/confirm a newsletter subscription. - * - * The `option` property controls what should happen: - * * `direct`: The subscription is directly active and does not need a confirmation. - * * `subscribe`: An email will be send to the provided email addrees containing a link to the /newsletter/confirm route. - * The subscription is only successful, if the /newsletter/confirm route is called with the generated hashes. - * * `unsubscribe`: The email address will be removed from the newsletter subscriptions. - * * `confirmSubscribe`: Confirmes the newsletter subscription for the provided email address. - */ - subscribeToNewsletter: { - requestBody: { - content: { - 'application/json': { - /** City */ - city?: string; - /** Custom field data that should be added to the subscription. */ - customFields?: string; - /** Email address that will receive the confirmation and the newsletter. */ - email: string; - /** First name */ - firstName?: string; - /** Identifier of the language. */ - languageId?: unknown; - /** Last name */ - lastName?: string; - /** Defines what should be done. */ - option: unknown; - /** Identifier of the salutation. */ - salutationId?: unknown; - /** Url of the storefront of the shop. This will be used for generating the link to the /newsletter/confirm inside the confirm email. */ - storefrontUrl: string; - /** Street */ - street?: string; - /** Zip code */ - tags?: string; - /** Zip code */ - zipCode?: string; - }; - }; - }; - responses: { - /** Success */ - 200: never; - }; - }; - /** - * Remove a newsletter subscription - * Removes a newsletter recipient from the mailing lists. - */ - unsubscribeToNewsletter: { - requestBody: { - content: { - 'application/json': { - /** Email address that should be removed from the mailing lists. */ - email: string; - }; - }; - }; - responses: { - /** Unsubscribing was successful. */ - 200: never; - }; - }; - /** - * Fetch a list of orders - * List orders of a customer. - */ - readOrder: { - requestBody: { - content: { - 'application/json': components['schemas']['Criteria'] & { - /** Check if the payment method of the order is still changeable. */ - checkPromotion?: boolean; - }; - }; - }; - responses: { - /** An array of orders and an indicator if the payment of the order can be changed. */ - 200: { - content: { - 'application/json': components['schemas']['OrderRouteResponse']; - }; - }; - }; - }; - /** - * Download a purchased file - * Download a file included in the given order and with the given id. Access must be granted. - */ - orderDownloadFile: { - parameters: { - path: { - orderId: string; - downloadId: string; - }; - }; - responses: { - /** An arbitrary binary file. */ - 200: { - content: { - 'application/octet-stream': string; - }; - }; - }; - }; - /** - * Update the payment method of an order - * Changes the payment method of a specific order. You can use the /order route to find out if the payment method of an order can be changed - take a look at the `paymentChangeable`- array in the response. - */ - orderSetPayment: { - requestBody: { - content: { - 'application/json': { - /** The identifier of the order. */ - orderId: string; - /** The identifier of the paymentMethod to be set */ - paymentMethodId: string; - }; - }; - }; - responses: { - /** Successfully updated the payment method of the order. */ - 200: { - content: { - 'application/json': components['schemas']['SuccessResponse']; - }; - }; - }; - }; - /** - * Cancel an order - * Cancels an order. The order state will be set to 'cancelled'. - */ - cancelOrder: { - requestBody: { - content: { - 'application/json': { - /** The identifier of the order to be canceled. */ - orderId?: string; - }; - }; - }; - responses: { - /** - * Returns the state of the state machine - * - * example: More information about the state machine can be found in the corresponding guide: [Using the state machine](https://developer.shopware.com/docs/guides/plugins/plugins/checkout/order/using-the-state-machine) - */ - 200: { - content: { - 'application/json': components['schemas']['StateMachineState']; - }; - }; - }; - }; - /** Loads all available payment methods */ - readPaymentMethod: { - requestBody: { - content: { - 'application/json': components['schemas']['Criteria'] & { - /** List only available */ - onlyAvailable?: boolean; - }; - }; - }; - responses: { - 200: { - content: { - 'application/json': { - /** aggregation result */ - aggregations?: GenericRecord; - elements?: components['schemas']['PaymentMethod'][]; - /** Total amount */ - total?: number; - }; - }; - }; - }; - }; - /** - * Fetch a list of products - * List products that match the given criteria. For performance ressons a limit should always be set. - */ - readProduct: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing products */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Product'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** Export product export */ - readProductExport: { - parameters: { - path: { - /** Access Key */ - accessKey: string; - /** File Name */ - fileName: string; - }; - }; - responses: { - 200: never; - }; - }; - /** - * Fetch a product listing by category - * Fetches a product listing for a specific category. It also provides filters, sortings and property aggregations, analogous to the /search endpoint. - */ - readProductListing: { - parameters: { - path: { - /** Identifier of a category. */ - categoryId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['ProductListingCriteria'] & - components['schemas']['ProductListingFlags']; - }; - }; - responses: { - /** Returns a product listing containing all products and additional fields to display a listing. */ - 200: { - content: { - 'application/json': components['schemas']['ProductListingResult']; - }; - }; - }; - }; - /** - * Fetch a single product - * This route is used to load a single product with the corresponding details. In addition to loading the data, the best variant of the product is determined when a parent id is passed. - */ - readProductDetail: { - parameters: { - path: { - /** Product ID */ - productId: string; - }; - }; - responses: { - /** Product information along with variant groups and options */ - 200: { - content: { - 'application/json': components['schemas']['ProductDetailResponse']; - }; - }; - }; - }; - /** - * Fetch cross-selling groups of a product - * This route is used to load the cross sellings for a product. A product has several cross selling definitions in which several products are linked. The route returns the cross sellings together with the linked products - */ - readProductCrossSellings: { - parameters: { - path: { - /** Product ID */ - productId: string; - }; - }; - responses: { - /** Found cross sellings */ - 200: { - content: { - 'application/json': components['schemas']['CrossSellingElementCollection']; - }; - }; - }; - }; - /** - * Search for a matching variant by product options. - * Performs a search for product variants and returns the best matching variant. - */ - searchProductVariantIds: { - parameters: { - path: { - /** Product ID */ - productId: string; - }; - }; - requestBody?: { - content: { - 'application/json': { - /** The options parameter for the variant to find. */ - options: string[]; - /** The id of the option group that has been switched. */ - switchedGroup?: string; - }; - }; - }; - responses: { - /** Returns an FoundCombination struct containing the ids matching the search. */ - 200: { - content: { - 'application/json': components['schemas']['FindProductVariantRouteResponse']; - }; - }; - }; - }; - /** - * Save a product review - * Saves a review for a product. Reviews have to be activated in the settings. - */ - saveProductReview: { - parameters: { - path: { - /** Identifier of the product which is reviewed. */ - productId: string; - }; - }; - requestBody?: { - content: { - 'application/json': { - /** The content of review. */ - content: unknown; - /** The email address of the review author. If not set, the email of the customer is chosen. */ - email?: string; - /** The name of the review author. If not set, the first name of the customer is chosen. */ - name?: string; - /** The review rating for the product. */ - points: unknown; - /** The title of the review. */ - title: unknown; - }; - }; - }; - responses: { - /** Success response indicating the review was saved successfuly. */ - 200: never; - }; - }; - /** - * Fetch product reviews - * Perform a filtered search for product reviews. - */ - readProductReviews: { - parameters: { - path: { - /** Identifier of the product. */ - productId: string; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing product reviews */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['ProductReview'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** - * Fetch salutations - * Perform a filtered search for salutations. - */ - readSalutation: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing salutations. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['Salutation'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - }; - }; - /** Access point for different api logics which are provided by apps over script hooks */ - postScriptStoreApiRoute: { - parameters: { - path: { - /** Dynamic hook which used to build the hook name */ - hook: string; - }; - }; - responses: { - /** Returns different structures of results based on the called script. */ - 200: never; - }; - }; - /** - * Search for products - * Performs a search for products which can be used to display a product listing. - */ - searchPage: { - requestBody?: { - content: { - 'application/json': { - /** Using the search parameter, the server performs a text search on all records based on their data model and weighting as defined in the entity definition using the SearchRanking flag. */ - search: string; - } & components['schemas']['ProductListingCriteria'] & - components['schemas']['ProductListingFlags']; - }; - }; - responses: { - /** Returns a product listing containing all products and additional fields to display a listing. */ - 200: { - content: { - 'application/json': components['schemas']['ProductListingResult']; - }; - }; - }; - }; - /** - * Search for products (suggest) - * Can be used to implement search previews or suggestion listings, that don’t require any interaction. - */ - searchSuggest: { - requestBody: { - content: { - 'application/json': { - /** Using the search parameter, the server performs a text search on all records based on their data model and weighting as defined in the entity definition using the SearchRanking flag. */ - search: string; - } & components['schemas']['ProductListingFlags']; - }; - }; - responses: { - /** - * Returns a product listing containing all products and additional fields. - * - * Note: Aggregations, currentFilters and availableSortings are empty in this response. If you need them to display a listing, use the /search route instead. - */ - 200: { - content: { - 'application/json': components['schemas']['ProductListingResult']; - }; - }; - }; - }; - /** - * Fetch SEO routes - * Perform a filtered search for seo urls. - */ - readSeoUrl: { - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - /** Entity search result containing seo urls. */ - 200: { - content: { - 'application/json': { - elements?: components['schemas']['SeoUrl'][]; - } & components['schemas']['EntitySearchResult']; - }; - }; - 404: components['responses']['404']; - }; - }; - /** - * Fetch shipping methods - * Perform a filtered search for shipping methods. - */ - readShippingMethod: { - parameters: { - query?: { - /** List only available shipping methods. This filters shipping methods methods which can not be used in the actual context because of their availability rule. */ - onlyAvailable?: boolean; - }; - }; - requestBody?: { - content: { - 'application/json': components['schemas']['Criteria']; - }; - }; - responses: { - 200: { - content: { - 'application/json': { - /** aggregation result */ - aggregations?: GenericRecord; - elements?: components['schemas']['ShippingMethod'][]; - /** Total amount */ - total?: number; - }; - }; - }; - }; - }; - /** - * Fetch sitemaps - * Fetches a list of compressed sitemap files, which are often used by search engines. - */ - readSitemap: { - responses: { - /** Returns a list of available sitemaps. */ - 200: { - content: { - 'application/json': components['schemas']['Sitemap'][]; - }; - }; - }; - }; -}; - -export type operationPaths = - | 'readCategoryList post /category' - | 'readCategory post /category/{navigationId}?slots' - | 'readSeoUrl post /seo-url' - | 'readContext get /context' - | 'updateContext patch /context' - | 'readCurrency post /currency' - | 'handlePaymentMethod post /handle-payment' - | 'readLanguages post /language' - | 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls' - | 'readPaymentMethod post /payment-method' - | 'confirmNewsletter post /newsletter/confirm' - | 'subscribeToNewsletter post /newsletter/subscribe' - | 'unsubscribeToNewsletter post /newsletter/unsubscribe' - | 'readCountry post /country' - | 'sendContactMail post /contact-form' - | 'readProductExport get /product-export/{accessKey}/{fileName}' - | 'getCustomerGroupRegistrationInfo get /customer-group-registration/config/{customerGroupId}' - | 'readCms post /cms/{id}' - | 'readSalutation post /salutation' - | 'readCountryState post /country-state/{countryId}' - | 'postScriptStoreApiRoute post /script/{hook}' - | 'download post /document/download/{documentId}/{deepLinkCode}' - | 'readNewsletterRecipient post /account/newsletter-recipient' - | 'changeProfile post /account/change-profile' - | 'changeEmail post /account/change-email' - | 'changeLanguage post /account/change-language' - | 'changePassword post /account/change-password' - | 'changePaymentMethod post /account/change-payment-method/{paymentMethodId}' - | 'getCustomerRecoveryIsExpired post /account/customer-recovery-is-expired' - | 'readCustomer post /account/customer' - | 'deleteCustomer delete /account/customer' - | 'deleteCustomerAddress delete /account/address/{addressId}' - | 'updateCustomerAddress patch /account/address/{addressId}' - | 'listAddress post /account/list-address' - | 'loginCustomer post /account/login' - | 'logoutCustomer post /account/logout' - | 'registerConfirm post /account/register-confirm' - | 'register post /account/register' - | 'recoveryPassword post /account/recovery-password-confirm' - | 'sendRecoveryMail post /account/recovery-password' - | 'defaultShippingAddress patch /account/address/default-shipping/{addressId}' - | 'defaultBillingAddress patch /account/address/default-billing/{addressId}' - | 'createCustomerAddress post /account/address' - | 'readProductCrossSellings post /product/{productId}/cross-selling' - | 'readProductDetail post /product/{productId}' - | 'readProduct post /product' - | 'readProductReviews post /product/{productId}/reviews' - | 'saveProductReview post /product/{productId}/review' - | 'searchProductVariantIds post /product/{productId}/find-variant' - | 'readSitemap get /sitemap' - | 'searchPage post /search' - | 'readProductListing post /product-listing/{categoryId}' - | 'readShippingMethod post /shipping-method?onlyAvailable' - | 'addProductOnWishlist post /customer/wishlist/add/{productId}' - | 'readCustomerWishlist post /customer/wishlist' - | 'mergeProductOnWishlist post /customer/wishlist/merge' - | 'deleteProductOnWishlist delete /customer/wishlist/delete/{productId}' - | 'readCart get /checkout/cart?name' - | 'deleteCart delete /checkout/cart' - | 'addLineItem post /checkout/cart/line-item' - | 'removeLineItem delete /checkout/cart/line-item?ids' - | 'updateLineItem patch /checkout/cart/line-item' - | 'createOrder post /checkout/order' - | 'cancelOrder post /order/state/cancel' - | 'readOrder post /order' - | 'orderSetPayment post /order/payment' - | 'orderDownloadFile get /order/download/{orderId}/{downloadId}' - | 'readLandingPage post /landing-page/{landingPageId}' - | 'searchSuggest post /search-suggest'; diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 39fb9a479..f3bbb2ca6 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,6 +1,6 @@ -import { createAPIClient, RequestReturnType, RequestParameters } from '@shopware/api-client'; -//import { operations, operationPaths } from './api-types/apiTypes-6.5.2.0'; -import { operations, operationPaths } from '@shopware/api-client/api-types'; +import { createAPIClient, RequestReturnType } from '@shopware/api-client'; +import { operations } from '@shopware/api-client/api-types'; +import { extendedPaths, extendedOperations } from './api-extended'; import { ApiSchemas, CategoryListingResultSW, @@ -12,20 +12,12 @@ import { const domainSW = `https://${process.env.SHOPWARE_STORE_DOMAIN!}/${process.env.SHOPWARE_API_TYPE!}`; const accessTokenSW = `${process.env.SHOPWARE_ACCESS_TOKEN}`; -const apiInstance = createAPIClient({ +const apiInstance = createAPIClient({ baseURL: domainSW, accessToken: accessTokenSW, apiType: 'store-api' }); -export type ExtendedOperations = {}; - -// reimport operations request parameters to use it in application -export type ApiRequestParams = RequestParameters< - ExtendedOperations, - operations ->; - // reimport operations return types to use it in application export type ApiReturnType = RequestReturnType< OPERATION_NAME, diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts index 8f0a1ae7a..4a6f9d875 100644 --- a/lib/shopware/types.ts +++ b/lib/shopware/types.ts @@ -1,17 +1,15 @@ import { components } from '@shopware/api-client/api-types'; +import { ExtendedCriteria } from './api-extended'; /** Shopware Types */ /** Schemas */ export type ApiSchemas = components['schemas']; export type StoreNavigationTypeSW = 'main-navigation' | 'footer-navigation' | 'service-navigation'; -export type ProductListingCriteria = ApiSchemas['ProductListingCriteria'] & { +export type ProductListingCriteria = { query: string; -} & Omit & { Criteria: { filter?: { - field: string; - type: string; - value: string | null | boolean; -}[]}}; +} & Omit & + ExtendedCriteria; /** Return Types */ export type CategoryListingResultSW = { From 1cdb5d3343a3ade486dfd2ebb2aefadab64a189c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 10 Jul 2023 16:24:01 +0200 Subject: [PATCH 09/57] feat(poc): fix type errors in transform, add simple sitemap --- app/sitemap.ts | 23 +---- lib/shopware/api-extended.ts | 172 +++++++++++++++++++++++++++++++++-- lib/shopware/api.ts | 49 +++++++--- lib/shopware/index.ts | 30 ++++-- lib/shopware/transform.ts | 26 ++++-- lib/shopware/types.ts | 7 +- 6 files changed, 246 insertions(+), 61 deletions(-) diff --git a/app/sitemap.ts b/app/sitemap.ts index d8cdfd2ea..d1b685ff1 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,4 +1,4 @@ -import { getCollections, getPages, getProducts } from 'lib/shopify'; +import { getProductSeoUrls } from 'lib/shopware'; import { MetadataRoute } from 'next'; const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL @@ -11,29 +11,16 @@ export default async function sitemap(): Promise - collections.map((collection) => ({ - url: `${baseUrl}${collection.path}`, - lastModified: collection.updatedAt - })) - ); - - const productsPromise = getProducts({}).then((products) => + // @ToDo: Get categories and get cms pages + const productsPromise = getProductSeoUrls().then((products) => products.map((product) => ({ - url: `${baseUrl}/product/${product.handle}`, + url: `${baseUrl}/product/${product.path}`, lastModified: product.updatedAt })) ); - const pagesPromise = getPages().then((pages) => - pages.map((page) => ({ - url: `${baseUrl}/${page.handle}`, - lastModified: page.updatedAt - })) - ); - const fetchedRoutes = ( - await Promise.all([collectionsPromise, productsPromise, pagesPromise]) + await Promise.all([productsPromise]) ).flat(); return [...routesMap, ...fetchedRoutes]; diff --git a/lib/shopware/api-extended.ts b/lib/shopware/api-extended.ts index b912ced0c..745325bd5 100644 --- a/lib/shopware/api-extended.ts +++ b/lib/shopware/api-extended.ts @@ -1,17 +1,73 @@ import { operations, operationPaths, components } from '@shopware/api-client/api-types'; +type schemas = components['schemas']; type operationsWithoutOriginal = Omit< operations, - 'readProduct' | 'searchPage' | 'readProductListing' + | 'readCategory' + | 'readCategoryList' + | 'readNavigation' + | 'readProduct' + | 'readProductCrossSellings' + | 'readProductListing' + | 'searchPage' >; -export type extendedPaths = 'readProduct post /product' | operationPaths; +export type extendedPaths = + | 'readCategory post /category/{navigationId}?slots' + | 'readCategoryList post /category' + | 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls' + | 'readProduct post /product' + | 'readProductCrossSellings post /product/{productId}/cross-selling' + | 'readProductListing post /product-listing/{categoryId}' + | 'searchPage post /search' + | operationPaths; export type extendedOperations = operationsWithoutOriginal & { + readCategory: extendedReadCategory; + readCategoryList: extendedReadCategoryList; + readNavigation: extendedReadNavigation; readProduct: extendedReadProduct; - searchPage: extendedSearchPage; + readProductCrossSellings: extendedReadProductCrossSellings; readProductListing: extendedReadProductListing; + searchPage: extendedSearchPage; }; -export type ExtendedCriteria = Omit & { +export type ExtendedCmsBlock = Omit & { + slots?: schemas['CmsSlot'][]; +}; +export type ExtendedCmsSection = Omit & { + blocks?: ExtendedCmsBlock[]; +}; +export type ExtendedCmsPage = Omit & { + sections?: ExtendedCmsSection[]; +}; + +export type ExtendedProduct = Omit< + schemas['Product'], + 'children' | 'seoUrls' | 'options' | 'media' +> & { + children?: ExtendedProduct[]; + seoUrls?: schemas['SeoUrl'][]; + options?: schemas['PropertyGroupOption'][]; + media?: schemas['ProductMedia'][]; +}; + +export type ExtendedProductListingResult = Omit & { + elements?: ExtendedProduct[]; +}; + +export type ExtendedCrossSellingElementCollection = Omit< + schemas['CrossSellingElementCollection'], + 'products' +> & { + products?: ExtendedProduct[]; +}; + +export type ExtendedCategory = Omit & { + children?: ExtendedCategory[]; + seoUrls?: schemas['SeoUrl'][]; + cmsPage?: ExtendedCmsPage; +}; + +export type ExtendedCriteria = Omit & { filter?: { field: string; type: string; @@ -30,8 +86,25 @@ type extendedReadProduct = { 200: { content: { 'application/json': { - elements?: components['schemas']['Product'][]; - } & components['schemas']['EntitySearchResult']; + elements?: ExtendedProduct[]; + } & schemas['EntitySearchResult']; + }; + }; + }; +}; + +type extendedReadProductCrossSellings = { + parameters: { + path: { + /** Product ID */ + productId: string; + }; + }; + responses: { + /** Found cross sellings */ + 200: { + content: { + 'application/json': ExtendedCrossSellingElementCollection }; }; }; @@ -106,14 +179,93 @@ type extendedSearchPage = { /** Using the search parameter, the server performs a text search on all records based on their data model and weighting as defined in the entity definition using the SearchRanking flag. */ search: string; } & ExtendedProductCriteria & - components['schemas']['ProductListingFlags']; + schemas['ProductListingFlags']; }; }; responses: { /** Returns a product listing containing all products and additional fields to display a listing. */ 200: { content: { - 'application/json': components['schemas']['ProductListingResult']; + 'application/json': ExtendedProductListingResult; + }; + }; + }; +}; + +type extendedReadNavigation = { + parameters: { + header?: { + /** Instructs Shopware to try and resolve SEO URLs for the given navigation item */ + 'sw-include-seo-urls'?: boolean; + }; + path: { + /** Identifier of the active category in the navigation tree (if not used, just set to the same as rootId). */ + activeId: string; + /** Identifier of the root category for your desired navigation tree. You can use it to fetch sub-trees of your navigation tree. */ + rootId: string; + }; + }; + requestBody: { + content: { + 'application/json': ExtendedCriteria & { + /** Return the categories as a tree or as a flat list. */ + buildTree?: unknown; + /** Determines the depth of fetched navigation levels. */ + depth?: unknown; + }; + }; + }; + responses: { + /** All available navigations */ + 200: { + content: { + 'application/json': ExtendedCategory[]; + }; + }; + }; +}; + +type extendedReadCategory = { + parameters: { + query?: { + /** Resolves only the given slot identifiers. The identifiers have to be seperated by a '|' character */ + slots?: string; + }; + path: { + /** Identifier of the category to be fetched */ + navigationId: string; + }; + }; + requestBody?: { + content: { + 'application/json': ExtendedCriteria & + Omit & + ExtendedCriteria; + }; + }; + responses: { + /** The loaded category with cms page */ + 200: { + content: { + 'application/json': ExtendedCategory; + }; + }; + }; +}; + +type extendedReadCategoryList = { + requestBody?: { + content: { + 'application/json': ExtendedCriteria; + }; + }; + responses: { + /** Entity search result containing categories. */ + 200: { + content: { + 'application/json': { + elements?: ExtendedCategory[]; + } & components['schemas']['EntitySearchResult']; }; }; }; @@ -128,14 +280,14 @@ type extendedReadProductListing = { }; requestBody?: { content: { - 'application/json': ExtendedProductCriteria & components['schemas']['ProductListingFlags']; + 'application/json': ExtendedProductCriteria & schemas['ProductListingFlags']; }; }; responses: { /** Returns a product listing containing all products and additional fields to display a listing. */ 200: { content: { - 'application/json': components['schemas']['ProductListingResult']; + 'application/json': ExtendedProductListingResult; }; }; }; diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index f3bbb2ca6..6cbd1bbf8 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,10 +1,17 @@ import { createAPIClient, RequestReturnType } from '@shopware/api-client'; import { operations } from '@shopware/api-client/api-types'; -import { extendedPaths, extendedOperations } from './api-extended'; import { - ApiSchemas, + ExtendedCategory, + ExtendedCriteria, + ExtendedCrossSellingElementCollection, + ExtendedProductListingResult, + extendedOperations, + extendedPaths +} from './api-extended'; +import { CategoryListingResultSW, ProductListingCriteria, + RouteNames, SeoURLResultSW, StoreNavigationTypeSW } from './types'; @@ -27,7 +34,7 @@ export type ApiReturnType = RequestRetu export async function requestNavigation( type: StoreNavigationTypeSW, depth: number -): Promise { +): Promise { return await apiInstance.invoke( 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls', { @@ -41,7 +48,7 @@ export async function requestNavigation( export async function requestCategory( categoryId: string, criteria?: Partial -): Promise { +): Promise { return await apiInstance.invoke('readCategory post /category/{navigationId}?slots', { navigationId: categoryId, criteria @@ -49,21 +56,21 @@ export async function requestCategory( } export async function requestCategoryList( - criteria: Partial + criteria: Partial ): Promise { return await apiInstance.invoke('readCategoryList post /category', criteria); } export async function requestProductsCollection( criteria: Partial -): Promise { +): Promise { return await apiInstance.invoke('readProduct post /product', criteria); } export async function requestCategoryProductsCollection( categoryId: string, criteria: Partial -): Promise { +): Promise { return await apiInstance.invoke('readProductListing post /product-listing/{categoryId}', { ...criteria, categoryId: categoryId @@ -72,17 +79,35 @@ export async function requestCategoryProductsCollection( export async function requestSearchCollectionProducts( criteria?: Partial -): Promise { +): Promise { return await apiInstance.invoke('searchPage post /search', { search: encodeURIComponent(criteria?.query || ''), ...criteria }); } -export async function requestSeoUrl(handle: string): Promise { +export async function requestSeoUrls(routeName: RouteNames, page: number = 1, limit: number = 100) { return await apiInstance.invoke('readSeoUrl post /seo-url', { - page: 1, - limit: 1, + page: page, + limit: limit, + filter: [ + { + type: 'equals', + field: 'routeName', + value: routeName + } + ] + }); +} + +export async function requestSeoUrl( + handle: string, + page: number = 1, + limit: number = 1 +): Promise { + return await apiInstance.invoke('readSeoUrl post /seo-url', { + page: page, + limit: limit, filter: [ { type: 'multi', @@ -108,7 +133,7 @@ export async function requestSeoUrl(handle: string): Promise { export async function requestCrossSell( productId: string, criteria?: Partial -): Promise { +): Promise { return await apiInstance.invoke( 'readProductCrossSellings post /product/{productId}/cross-selling', { diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 4901c21cf..a6f6d5293 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -24,7 +24,8 @@ import { requestNavigation, requestProductsCollection, requestSearchCollectionProducts, - requestSeoUrl + requestSeoUrl, + requestSeoUrls } from './api'; import { transformCollection, @@ -35,6 +36,7 @@ import { transformProducts, transformStaticCollection } from './transform'; +import { ExtendedCategory, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; export async function getMenu(params?: { type?: StoreNavigationTypeSW; @@ -66,11 +68,9 @@ export async function getFirstSeoUrlElement( } } -export async function getFirstProduct( - productId: string -): Promise { +export async function getFirstProduct(productId: string): Promise { const productCriteria = getDefaultProductCriteria(productId); - const res: ApiSchemas['ProductListingResult'] = await requestProductsCollection(productCriteria); + const res: ExtendedProductListingResult = await requestProductsCollection(productCriteria); if (res.elements && res.elements.length > 0 && res.elements[0]) { return res.elements[0]; } @@ -142,7 +142,7 @@ export async function getCollectionProducts(params?: { export async function getCategory( seoUrl: ApiSchemas['SeoUrl'], cms: boolean = false -): Promise { +): Promise { const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria(); const resCategory = await requestCategory(seoUrl.foreignKey, criteria); @@ -165,8 +165,21 @@ export async function getCollection(handle: string | []) { } } +export async function getProductSeoUrls() { + const productSeoUrls: { path: string; updatedAt: string }[] = []; + const res = await requestSeoUrls('frontend.detail.page'); + + if (res.elements && res.elements.length > 0) { + res.elements.map((item) => + productSeoUrls.push({ path: item.seoPathInfo, updatedAt: item.updatedAt ?? item.createdAt }) + ); + } + + return productSeoUrls; +} + export async function getProduct(handle: string | []): Promise { - let productSW: ApiSchemas['Product'] | undefined; + let productSW: ExtendedProduct | undefined; let productId: string | undefined; const productHandle = transformHandle(handle); @@ -189,11 +202,12 @@ export async function getProduct(handle: string | []): Promise { - let products: ApiSchemas['ProductListingResult'] = {}; + let products: ExtendedProductListingResult = {}; const res = await requestCrossSell(productId, getDefaultCrossSellingCriteria()); // @ToDo: Make this more dynamic to merge multiple Cross-Sellings, at the moment we only get the first one if (res && res[0] && res[0].products) { + // @ts-ignore (@ToDo: fix this wrong type ...) products.elements = res[0].products; } diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index 9bff11aad..e67906678 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -8,9 +8,15 @@ import { ProductOption, ProductVariant } from './types'; +import { + ExtendedCategory, + ExtendedCmsPage, + ExtendedProduct, + ExtendedProductListingResult +} from './api-extended'; import { ListItem } from 'components/layout/search/filter'; -export function transformMenu(res: ApiSchemas['NavigationRouteResponse'], type: string) { +export function transformMenu(res: ExtendedCategory[], type: string) { let menu: Menu[] = []; res.map((item) => menu.push(transformMenuItem(item, type))); @@ -18,7 +24,7 @@ export function transformMenu(res: ApiSchemas['NavigationRouteResponse'], type: return menu; } -function transformMenuItem(item: ApiSchemas['Category'], type: string): Menu { +function transformMenuItem(item: ExtendedCategory, type: string): Menu { // @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item) return { id: item.id ?? '', @@ -36,11 +42,11 @@ function transformMenuItem(item: ApiSchemas['Category'], type: string): Menu { export function transformPage( seoUrlElement: ApiSchemas['SeoUrl'], - category: ApiSchemas['Category'] + category: ExtendedCategory ): Page { let plainHtmlContent; if (category.cmsPage) { - const cmsPage: ApiSchemas['CmsPage'] = category.cmsPage; + const cmsPage: ExtendedCmsPage = category.cmsPage; plainHtmlContent = transformToPlainHtmlContent(cmsPage); } @@ -62,7 +68,7 @@ export function transformPage( }; } -export function transformToPlainHtmlContent(cmsPage: ApiSchemas['CmsPage']): string { +export function transformToPlainHtmlContent(cmsPage: ExtendedCmsPage): string { let plainHtmlContent = ''; cmsPage.sections?.map((section) => { @@ -84,7 +90,7 @@ export function transformToPlainHtmlContent(cmsPage: ApiSchemas['CmsPage']): str export function transformCollection( seoUrlElement: ApiSchemas['SeoUrl'], - resCategory: ApiSchemas['Category'] + resCategory: ExtendedCategory ) { return { handle: seoUrlElement.seoPathInfo, @@ -137,7 +143,7 @@ export function transformStaticCollectionToList(collection: Collection[]): ListI return listItem; } -export function transformProducts(res: ApiSchemas['ProductListingResult']): Product[] { +export function transformProducts(res: ExtendedProductListingResult): Product[] { let products: Product[] = []; if (res.elements && res.elements.length > 0) { @@ -147,7 +153,7 @@ export function transformProducts(res: ApiSchemas['ProductListingResult']): Prod return products; } -export function transformProduct(item: ApiSchemas['Product']): Product { +export function transformProduct(item: ExtendedProduct): Product { const productOptions = transformOptions(item); const productVariants = transformVariants(item); @@ -202,7 +208,7 @@ export function transformProduct(item: ApiSchemas['Product']): Product { }; } -function transformOptions(parent: ApiSchemas['Product']): ProductOption[] { +function transformOptions(parent: ExtendedProduct): ProductOption[] { // we only transform options for parents with children, ignore child products with options let productOptions: ProductOption[] = []; if (parent.children && parent.parentId === null && parent.children.length > 0) { @@ -231,7 +237,7 @@ function transformOptions(parent: ApiSchemas['Product']): ProductOption[] { return productOptions; } -function transformVariants(parent: ApiSchemas['Product']): ProductVariant[] { +function transformVariants(parent: ExtendedProduct): ProductVariant[] { let productVariants: ProductVariant[] = []; if (parent.children && parent.parentId === null && parent.children.length > 0) { parent.children.map((child) => { diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts index 4a6f9d875..405ed0b42 100644 --- a/lib/shopware/types.ts +++ b/lib/shopware/types.ts @@ -1,5 +1,5 @@ import { components } from '@shopware/api-client/api-types'; -import { ExtendedCriteria } from './api-extended'; +import { ExtendedCriteria, ExtendedCategory, ExtendedCmsPage } from './api-extended'; /** Shopware Types */ @@ -10,10 +10,11 @@ export type ProductListingCriteria = { query: string; } & Omit & ExtendedCriteria; +export type RouteNames = 'frontend.navigation.page' | 'frontend.detail.page' | 'frontend.account.customer-group-registration.page' | 'frontend.landing.page' /** Return Types */ export type CategoryListingResultSW = { - elements?: ApiSchemas['Category'][]; + elements?: ExtendedCategory[]; } & ApiSchemas['EntitySearchResult']; export type SeoURLResultSW = { elements?: ApiSchemas['SeoUrl'][]; @@ -33,7 +34,7 @@ export type Page = { updatedAt: string; routeName?: string; foreignKey?: string; - originalCmsPage?: ApiSchemas['CmsPage']; + originalCmsPage?: ExtendedCmsPage; }; export type ProductOption = { From 7ca79902d3181422d839fd4ffe6b49f026a22fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Tue, 11 Jul 2023 08:54:30 +0200 Subject: [PATCH 10/57] feat(poc): fix type error for crossSell --- lib/shopware/api-extended.ts | 2 +- lib/shopware/index.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/shopware/api-extended.ts b/lib/shopware/api-extended.ts index 745325bd5..07845b473 100644 --- a/lib/shopware/api-extended.ts +++ b/lib/shopware/api-extended.ts @@ -59,7 +59,7 @@ export type ExtendedCrossSellingElementCollection = Omit< 'products' > & { products?: ExtendedProduct[]; -}; +}[]; export type ExtendedCategory = Omit & { children?: ExtendedCategory[]; diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index a6f6d5293..717380b19 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -207,7 +207,6 @@ export async function getProductRecommendations(productId: string): Promise Date: Wed, 12 Jul 2023 10:02:08 +0200 Subject: [PATCH 11/57] feat(poc): add pagination to category page --- .../(collection)/[...collection]/page.tsx | 14 ++-- components/collection/pagination.tsx | 68 +++++++++++++++++++ components/grid/three-items.tsx | 2 +- components/layout/search/filter/item.tsx | 2 + lib/shopware/index.ts | 6 +- package.json | 3 +- pnpm-lock.yaml | 14 +++- 7 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 components/collection/pagination.tsx diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index 1f8d4dfbf..67e3c326d 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import Grid from 'components/grid'; import ProductGridItems from 'components/layout/product-grid-items'; +import Pagination from 'components/collection/pagination'; import { defaultSort, sorting } from 'lib/constants'; export const runtime = 'edge'; @@ -34,7 +35,7 @@ export default async function CategoryPage({ const { sort, page } = searchParams as { [key: string]: string }; const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort; - const products = await getCollectionProducts({ + const { products, total, limit } = await getCollectionProducts({ collection: params.collection, page: page ? parseInt(page) : 1, sortKey, @@ -46,9 +47,14 @@ export default async function CategoryPage({ {products.length === 0 ? (

{`No products found in this collection`}

) : ( - - - +
+ + + + +
)}
); diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx new file mode 100644 index 000000000..ad3692498 --- /dev/null +++ b/components/collection/pagination.tsx @@ -0,0 +1,68 @@ +'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 }) { + const router = useRouter(); + const pathname = usePathname(); + const currentParams = useSearchParams(); + const q = currentParams.get('q'); + const sort = currentParams.get('sort'); + const pageCount = Math.ceil(itemsTotal / itemsPerPage); + + // 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({ + ...(q && { q }), + ...(sort && { sort }), + page: newPage.toString(), + })); + } + router.replace(newUrl); + }; + + return ( + <> + + + ); +} + +type clickEvent = { + index: number | null; + selected: number; + nextSelectedPage: number | undefined; + event: object; + isPrevious: boolean; + isNext: boolean; + isBreak: boolean; + isActive: boolean; +} \ No newline at end of file diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index a47af2ed5..a7d2b9010 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -37,7 +37,7 @@ function ThreeItemGridItem({ export async function ThreeItemGrid() { // Collections that start with `hidden-*` are hidden from the search page. - const homepageItems = await getCollectionProducts({ + const { products: homepageItems } = await getCollectionProducts({ collection: 'Summer-BBQ/Hidden-Category' }); diff --git a/components/layout/search/filter/item.tsx b/components/layout/search/filter/item.tsx index 1a3b73ad3..6f02b9483 100644 --- a/components/layout/search/filter/item.tsx +++ b/components/layout/search/filter/item.tsx @@ -40,6 +40,7 @@ function SortFilterItem({ item }: { item: SortFilterItem }) { const searchParams = useSearchParams(); const [active, setActive] = useState(searchParams.get('sort') === item.slug); const q = searchParams.get('q'); + const page = searchParams.get('page'); useEffect(() => { setActive(searchParams.get('sort') === item.slug); @@ -51,6 +52,7 @@ function SortFilterItem({ item }: { item: SortFilterItem }) { pathname, new URLSearchParams({ ...(q && { q }), + ...(page && { page }), sort: item.slug }) ) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 717380b19..b46bdc920 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -109,7 +109,7 @@ export async function getCollectionProducts(params?: { sortKey?: string; categoryId?: string; defaultSearchCriteria?: Partial; -}): Promise { +}): Promise<{ products: Product[]; total: number; limit: number }> { let res; let category = params?.categoryId; const collectionName = transformHandle(params?.collection ?? ''); @@ -136,7 +136,9 @@ export async function getCollectionProducts(params?: { res = await requestCategoryProductsCollection(category, productsCriteria); } - return res ? transformProducts(res) : []; + return res + ? { products: transformProducts(res), total: res.total ?? 0, limit: res.limit ?? 0 } + : { products: [], total: 0, limit: 0 }; } export async function getCategory( diff --git a/package.json b/package.json index f749df541..e2a1e9fdd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "next": "13.4.9", "react": "18.2.0", "react-cookie": "^4.1.1", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-paginate": "^8.2.0" }, "devDependencies": { "@playwright/test": "^1.35.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1be262caa..7d0d18cd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,9 @@ dependencies: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-paginate: + specifier: ^8.2.0 + version: 8.2.0(react@18.2.0) devDependencies: '@playwright/test': @@ -2411,7 +2414,6 @@ packages: /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} @@ -2811,7 +2813,6 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} @@ -2846,6 +2847,15 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + /react-paginate@8.2.0(react@18.2.0): + resolution: {integrity: sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw==} + peerDependencies: + react: ^16 || ^17 || ^18 + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} From a1b435487f9134e7505dcc33f8988bf1b766d2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Wed, 12 Jul 2023 14:14:15 +0200 Subject: [PATCH 12/57] feat(poc): improve lint for commit --- .husky/pre-commit | 5 +++++ package.json | 7 ++++--- pnpm-lock.yaml | 13 +++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..7a8e246e3 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +pnpm run lint +npx lint-staged diff --git a/package.json b/package.json index e2a1e9fdd..edaf14907 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,13 @@ "pnpm": ">=7" }, "scripts": { - "dev": "next dev", "build": "next build", - "start": "next start", + "dev": "next dev", "lint": "next lint", - "lint-staged": "lint-staged", + "prepare": "husky install", "prettier": "prettier --write --ignore-unknown .", "prettier:check": "prettier --check --ignore-unknown .", + "start": "next start", "test": "pnpm lint && pnpm prettier:check", "test:e2e": "playwright test" }, @@ -45,6 +45,7 @@ "eslint-config-next": "^13.4.9", "eslint-config-prettier": "^8.8.0", "eslint-plugin-unicorn": "^47.0.0", + "husky": "^8.0.3", "lint-staged": "^13.2.3", "postcss": "^8.4.25", "prettier": "^2.8.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d0d18cd1..169c48afc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@headlessui/react': specifier: ^1.7.15 @@ -66,6 +70,9 @@ devDependencies: eslint-plugin-unicorn: specifier: ^47.0.0 version: 47.0.0(eslint@8.44.0) + husky: + specifier: ^8.0.3 + version: 8.0.3 lint-staged: specifier: ^13.2.3 version: 13.2.3 @@ -1840,6 +1847,12 @@ packages: engines: {node: '>=14.18.0'} dev: true + /husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} From 3c797b2d4fccc74a817ca6873bf3de0a1804c8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Wed, 12 Jul 2023 14:17:40 +0200 Subject: [PATCH 13/57] feat(poc): use const instead of let --- lib/shopware/index.ts | 2 +- lib/shopware/transform.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index b46bdc920..c9bee2058 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -204,7 +204,7 @@ export async function getProduct(handle: string | []): Promise { - let products: ExtendedProductListingResult = {}; + const products: ExtendedProductListingResult = {}; const res = await requestCrossSell(productId, getDefaultCrossSellingCriteria()); // @ToDo: Make this more dynamic to merge multiple Cross-Sellings, at the moment we only get the first one diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index e67906678..8b0c23d83 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -17,7 +17,7 @@ import { import { ListItem } from 'components/layout/search/filter'; export function transformMenu(res: ExtendedCategory[], type: string) { - let menu: Menu[] = []; + const menu: Menu[] = []; res.map((item) => menu.push(transformMenuItem(item, type))); @@ -105,7 +105,7 @@ export function transformCollection( } export function transformStaticCollection(resCategory: CategoryListingResultSW): Collection[] { - let collection: Collection[] = []; + const collection: Collection[] = []; if (resCategory.elements && resCategory.elements.length > 0) { resCategory.elements.map((item) => @@ -129,7 +129,7 @@ export function transformStaticCollection(resCategory: CategoryListingResultSW): } export function transformStaticCollectionToList(collection: Collection[]): ListItem[] { - let listItem: ListItem[] = []; + const listItem: ListItem[] = []; if (collection && collection.length > 0) { collection.map((item) => @@ -144,7 +144,7 @@ export function transformStaticCollectionToList(collection: Collection[]): ListI } export function transformProducts(res: ExtendedProductListingResult): Product[] { - let products: Product[] = []; + const products: Product[] = []; if (res.elements && res.elements.length > 0) { res.elements.map((item) => products.push(transformProduct(item))); @@ -210,9 +210,9 @@ export function transformProduct(item: ExtendedProduct): Product { function transformOptions(parent: ExtendedProduct): ProductOption[] { // we only transform options for parents with children, ignore child products with options - let productOptions: ProductOption[] = []; + const productOptions: ProductOption[] = []; if (parent.children && parent.parentId === null && parent.children.length > 0) { - let group: { [key: string]: string[] } = {}; + const group: { [key: string]: string[] } = {}; parent.children.map((child) => { child.options?.map((option) => { if (option && option.group) { @@ -238,11 +238,11 @@ function transformOptions(parent: ExtendedProduct): ProductOption[] { } function transformVariants(parent: ExtendedProduct): ProductVariant[] { - let productVariants: ProductVariant[] = []; + const productVariants: ProductVariant[] = []; if (parent.children && parent.parentId === null && parent.children.length > 0) { parent.children.map((child) => { if (child.id) { - let selectedOptions: { name: string; value: string }[] = []; + const selectedOptions: { name: string; value: string }[] = []; child.options?.map((option) => { if (option.group) { selectedOptions.push({ From 184f008f6f6eda477d867da2b903fc3326fabaa4 Mon Sep 17 00:00:00 2001 From: mkucmus Date: Wed, 12 Jul 2023 15:50:35 +0200 Subject: [PATCH 14/57] feat: cart handling --- components/cart/index.tsx | 26 ++++------ lib/shopware/api.ts | 8 ++- lib/shopware/index.ts | 100 ++++++++++++++++++++++++++++++-------- 3 files changed, 97 insertions(+), 37 deletions(-) diff --git a/components/cart/index.tsx b/components/cart/index.tsx index b75166bd2..ab2372af1 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -1,20 +1,14 @@ -import { createCart, getCart } from 'lib/shopify'; +import { getCart } from 'lib/shopware'; import { cookies } from 'next/headers'; import CartModal from './modal'; export default async function Cart() { - // const cartId = cookies().get('cartId')?.value; - // let cartIdUpdated = false; - // let cart; - // if (cartId) { - // cart = await getCart(cartId); - // } - // // If the `cartId` from the cookie is not set or the cart is empty - // // (old carts becomes `null` when you checkout), then get a new `cartId` - // // and re-fetch the cart. - // if (!cartId || !cart) { - // cart = await createCart(); - // cartIdUpdated = true; - // } - // return ; -} + const cartId = cookies().get('sw-context-token')?.value; + let cartIdUpdated = true; + const cart = await getCart(); + + if (cartId !== cart.id) { + cartIdUpdated = true; + } + return ; +} \ No newline at end of file diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 6cbd1bbf8..89c666674 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -4,9 +4,9 @@ import { ExtendedCategory, ExtendedCriteria, ExtendedCrossSellingElementCollection, - ExtendedProductListingResult, extendedOperations, - extendedPaths + extendedPaths, + ExtendedProductListingResult } from './api-extended'; import { CategoryListingResultSW, @@ -142,3 +142,7 @@ export async function requestCrossSell( } ); } + +export async function requestCart() { + return apiInstance.invoke('readCart get /checkout/cart?name', {}); +} \ No newline at end of file diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index c9bee2058..bb974a196 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -1,11 +1,17 @@ +import { Cart } from 'lib/shopify/types'; import { - ApiSchemas, - Menu, - Page, - Product, - ProductListingCriteria, - StoreNavigationTypeSW -} from './types'; + requestCart, + requestCategory, + requestCategoryList, + requestCategoryProductsCollection, + requestCrossSell, + requestNavigation, + requestProductsCollection, + requestSearchCollectionProducts, + requestSeoUrl, + requestSeoUrls +} from './api'; +import { ExtendedCategory, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; import { getDefaultCategoryCriteria, getDefaultCategoryWithCmsCriteria, @@ -16,17 +22,6 @@ import { getSortingCriteria, getStaticCollectionCriteria } from './criteria'; -import { - requestCategory, - requestCategoryList, - requestCategoryProductsCollection, - requestCrossSell, - requestNavigation, - requestProductsCollection, - requestSearchCollectionProducts, - requestSeoUrl, - requestSeoUrls -} from './api'; import { transformCollection, transformHandle, @@ -36,7 +31,14 @@ import { transformProducts, transformStaticCollection } from './transform'; -import { ExtendedCategory, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; +import { + ApiSchemas, + Menu, + Page, + Product, + ProductListingCriteria, + StoreNavigationTypeSW +} from './types'; export async function getMenu(params?: { type?: StoreNavigationTypeSW; @@ -214,3 +216,63 @@ export async function getProductRecommendations(productId: string): Promise { + const cartData = await requestCart(); + + let cart: Cart = { + checkoutUrl: 'https://frontends-demo.vercel.app', + cost: { + subtotalAmount: { + amount: cartData.price?.positionPrice?.toString() || '0', + currencyCode: 'EUR' + }, + totalAmount: { + amount: cartData.price?.totalPrice?.toString() || '0', + currencyCode: 'EUR' + }, + totalTaxAmount: { + amount: '0', + currencyCode: 'EUR' + } + }, + id: cartData.token || '', + lines: + cartData.lineItems?.map((lineItem) => ({ + id: lineItem.id || '', + quantity: lineItem.quantity, + cost: { + totalAmount: { + amount: (lineItem as any)?.price?.totalPrice || '' + } + }, + merchandise: { + id: lineItem.referencedId, + title: lineItem.label, + selectedOptions: [], + product: { + description: lineItem.description, + descriptionHtml: lineItem.description, + id: lineItem.referencedId, + images: [], + seo: { + description: lineItem.description, + title: lineItem.label + }, + availableForSale: true, + featuredImage: (lineItem as any).cover?.url, + handle: '', + options: [], + variants: [], + priceRange: {}, + tags: [], + title: lineItem.label, + updatedAt: (lineItem as any)?.payload?.updatedAt + } + } + })) || [], + totalQuantity: cartData.lineItems?.length || 0 + }; + + return cart; +} From 899737f765cd77613ab2e9bac0fb82c6c1acba7e Mon Sep 17 00:00:00 2001 From: mkucmus Date: Thu, 13 Jul 2023 11:40:21 +0200 Subject: [PATCH 15/57] fix: use referencedId for item id --- lib/shopware/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index bb974a196..b5938c8ed 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -239,7 +239,7 @@ export async function getCart(): Promise { id: cartData.token || '', lines: cartData.lineItems?.map((lineItem) => ({ - id: lineItem.id || '', + id: lineItem.referencedId || '', quantity: lineItem.quantity, cost: { totalAmount: { From 8dcf6db08f4e3d3d57b059d76c23067159893a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Wed, 12 Jul 2023 14:24:43 +0200 Subject: [PATCH 16/57] feat(poc): test commit --- components/collection/pagination.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx index ad3692498..2d560cf52 100644 --- a/components/collection/pagination.tsx +++ b/components/collection/pagination.tsx @@ -12,7 +12,7 @@ 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. + // Invoke when user click to request another page. test const handlePageClick = (event: clickEvent) => { const page = event.selected; const newPage = page + 1; From 8550185eaebe220b4bf0f39300927d03ef69c3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 12:05:07 +0200 Subject: [PATCH 17/57] feat(poc): carousel and improved sub-collections --- .../(collection)/[...collection]/page.tsx | 24 ++++-- app/search/layout.tsx | 14 +--- app/search/page.tsx | 32 +++++--- components/carousel.tsx | 63 ++++++++-------- components/collection/pagination.tsx | 40 ++++++---- components/layout/search/collections.tsx | 16 ++-- lib/shopware/criteria.ts | 2 +- lib/shopware/index.ts | 72 ++++++++++++------ lib/shopware/transform.ts | 73 +++++++++++++------ lib/shopware/types.ts | 30 ++++++++ 10 files changed, 239 insertions(+), 127 deletions(-) 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 ? (

{`No products found in this collection`}

) : ( -
- - - - +
+
+ +
+
+ + + + +
+
+ +
)} 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 ? ( - // {product.title} - // ) : null} - //
- //
- // {product.title} - //
- //
- // - // ))} - //
- //
- // ); + return ( +
+
+ {[...products, ...products].map((product, i) => ( + + {product.featuredImage ? ( + {product.title} + ) : null} +
+
+ {product.title} +
+
+ + ))} +
+
+ ); } 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 From b6cff9ba012e548ad09102fe19aa603a7d3b45c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 12:19:38 +0200 Subject: [PATCH 18/57] feat(poc): test commit --- components/collection/pagination.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx index 40ad782f3..bfd8aec31 100644 --- a/components/collection/pagination.tsx +++ b/components/collection/pagination.tsx @@ -20,7 +20,7 @@ export default function Pagination({ const sort = currentParams.get('sort'); const pageCount = Math.ceil(itemsTotal / itemsPerPage); - // Invoke when user click to request another page. + // Invoke when user click to request another page. test const handlePageClick = (event: clickEvent) => { const page = event.selected; const newPage = page + 1; From 33d780d427b5968596065b708f3b04d93cc4ab13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 13:21:20 +0200 Subject: [PATCH 19/57] feat(poc): update dependencies --- .husky/pre-commit | 0 lib/shopware/index.ts | 2 +- package.json | 15 ++- pnpm-lock.yaml | 222 ++++++++++++++++++++++-------------------- 4 files changed, 124 insertions(+), 115 deletions(-) mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index febe5e013..7af3cfa6e 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -237,7 +237,7 @@ export async function getCart(): Promise { const cartData = await requestCart(); // @ToDo: should be moved to transformCart function - let cart: Cart = { + const cart: Cart = { checkoutUrl: 'https://frontends-demo.vercel.app', cost: { subtotalAmount: { diff --git a/package.json b/package.json index edaf14907..ed0046c91 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,12 @@ "test": "pnpm lint && pnpm prettier:check", "test:e2e": "playwright test" }, - "git": { - "pre-commit": "lint-staged" - }, "lint-staged": { "*": "prettier --write --ignore-unknown" }, "dependencies": { "@headlessui/react": "^1.7.15", - "@shopware/api-client": "0.0.0-canary-20230706101754", + "@shopware/api-client": "0.0.0-canary-20230713092547", "@vercel/og": "^0.5.8", "clsx": "^1.2.1", "is-empty-iterable": "^3.0.0", @@ -34,11 +31,11 @@ "react-paginate": "^8.2.0" }, "devDependencies": { - "@playwright/test": "^1.35.1", + "@playwright/test": "^1.36.0", "@tailwindcss/typography": "^0.5.9", - "@types/node": "^20.4.1", + "@types/node": "^20.4.2", "@types/react": "18.2.14", - "@types/react-dom": "18.2.6", + "@types/react-dom": "18.2.7", "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.14", "eslint": "^8.44.0", @@ -48,8 +45,8 @@ "husky": "^8.0.3", "lint-staged": "^13.2.3", "postcss": "^8.4.25", - "prettier": "^2.8.8", - "prettier-plugin-tailwindcss": "^0.3.0", + "prettier": "^3.0.0", + "prettier-plugin-tailwindcss": "^0.4.0", "tailwindcss": "^3.3.2", "typescript": "5.1.6" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 169c48afc..8d7e59991 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@shopware/api-client': - specifier: 0.0.0-canary-20230706101754 - version: 0.0.0-canary-20230706101754 + specifier: 0.0.0-canary-20230713092547 + version: 0.0.0-canary-20230713092547 '@vercel/og': specifier: ^0.5.8 version: 0.5.8 @@ -38,20 +38,20 @@ dependencies: devDependencies: '@playwright/test': - specifier: ^1.35.1 - version: 1.35.1 + specifier: ^1.36.0 + version: 1.36.0 '@tailwindcss/typography': specifier: ^0.5.9 version: 0.5.9(tailwindcss@3.3.2) '@types/node': - specifier: ^20.4.1 - version: 20.4.1 + specifier: ^20.4.2 + version: 20.4.2 '@types/react': specifier: 18.2.14 version: 18.2.14 '@types/react-dom': - specifier: 18.2.6 - version: 18.2.6 + specifier: 18.2.7 + version: 18.2.7 '@vercel/git-hooks': specifier: ^1.0.0 version: 1.0.0 @@ -80,11 +80,11 @@ devDependencies: specifier: ^8.4.25 version: 8.4.25 prettier: - specifier: ^2.8.8 - version: 2.8.8 + specifier: ^3.0.0 + version: 3.0.0 prettier-plugin-tailwindcss: - specifier: ^0.3.0 - version: 0.3.0(prettier@2.8.8) + specifier: ^0.4.0 + version: 0.4.0(prettier@3.0.0) tailwindcss: specifier: ^3.3.2 version: 3.3.2 @@ -347,8 +347,8 @@ packages: fastq: 1.15.0 dev: true - /@pkgr/utils@2.4.1: - resolution: {integrity: sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==} + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} dependencies: cross-spawn: 7.0.3 @@ -359,13 +359,13 @@ packages: tslib: 2.6.0 dev: true - /@playwright/test@1.35.1: - resolution: {integrity: sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==} + /@playwright/test@1.36.0: + resolution: {integrity: sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==} engines: {node: '>=16'} hasBin: true dependencies: - '@types/node': 20.4.1 - playwright-core: 1.35.1 + '@types/node': 20.4.2 + playwright-core: 1.36.0 optionalDependencies: fsevents: 2.3.2 dev: true @@ -379,8 +379,8 @@ packages: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: true - /@shopware/api-client@0.0.0-canary-20230706101754: - resolution: {integrity: sha512-h7nCTWVu6bLbxdKT8vEJcUVKVu/RveaZ3M1PxrQAMiP6b2nQeqRh5+QlqTEFGk+Middy11MINH70Wp6GMx+Y+A==} + /@shopware/api-client@0.0.0-canary-20230713092547: + resolution: {integrity: sha512-+5dHwprTnpwtHQl3eletxpvMexqKf9N8fIiecqaeemrsWLkiFyAouP76UzYPVjEF54neOOaAI0DuIBvi2w4WXg==} dependencies: ofetch: 1.1.1 dev: false @@ -427,8 +427,8 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.4.1: - resolution: {integrity: sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==} + /@types/node@20.4.2: + resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==} dev: true /@types/normalize-package-data@2.4.1: @@ -438,8 +438,8 @@ packages: /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - /@types/react-dom@18.2.6: - resolution: {integrity: sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==} + /@types/react-dom@18.2.7: + resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} dependencies: '@types/react': 18.2.14 dev: true @@ -454,8 +454,8 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - /@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==} + /@typescript-eslint/parser@5.62.0(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -464,9 +464,9 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.61.0 - '@typescript-eslint/types': 5.61.0 - '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.6) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) debug: 4.3.4 eslint: 8.44.0 typescript: 5.1.6 @@ -474,21 +474,21 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@5.61.0: - resolution: {integrity: sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw==} + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.61.0 - '@typescript-eslint/visitor-keys': 5.61.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/types@5.61.0: - resolution: {integrity: sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ==} + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.61.0(typescript@5.1.6): - resolution: {integrity: sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw==} + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.1.6): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -496,23 +496,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.61.0 - '@typescript-eslint/visitor-keys': 5.61.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.3 + semver: 7.5.4 tsutils: 3.21.0(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/visitor-keys@5.61.0: - resolution: {integrity: sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg==} + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.1 dev: true @@ -636,7 +636,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 get-intrinsic: 1.2.1 is-string: 1.0.7 dev: true @@ -652,7 +652,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 es-shim-unscopables: 1.0.0 dev: true @@ -662,7 +662,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 es-shim-unscopables: 1.0.0 dev: true @@ -671,7 +671,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 dev: true @@ -693,7 +693,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.9 - caniuse-lite: 1.0.30001512 + caniuse-lite: 1.0.30001515 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -762,9 +762,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001512 - electron-to-chromium: 1.4.451 - node-releases: 2.0.12 + caniuse-lite: 1.0.30001515 + electron-to-chromium: 1.4.459 + node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true @@ -808,8 +808,8 @@ packages: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: false - /caniuse-lite@1.0.30001512: - resolution: {integrity: sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==} + /caniuse-lite@1.0.30001515: + resolution: {integrity: sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==} /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1083,8 +1083,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.451: - resolution: {integrity: sha512-YYbXHIBxAHe3KWvGOJOuWa6f3tgow44rBW+QAuwVp2DvGqNZeE//K2MowNdWS7XE8li5cgQDrX1LdBr41LufkA==} + /electron-to-chromium@1.4.459: + resolution: {integrity: sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg==} dev: true /emoji-regex@10.2.1: @@ -1113,8 +1113,8 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.21.2: - resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + /es-abstract@1.21.3: + resolution: {integrity: sha512-ZU4miiY1j3sGPFLJ34VJXEqhpmL+HGByCinGHv4HC+Fxl2fI2Z4yR6tl0mORnDr6PA8eihWo4LmSWDbvhALckg==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 @@ -1148,9 +1148,10 @@ packages: string.prototype.trim: 1.2.7 string.prototype.trimend: 1.0.6 string.prototype.trimstart: 1.0.6 + typed-array-byte-offset: 1.0.0 typed-array-length: 1.0.4 unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 + which-typed-array: 1.1.10 dev: true /es-set-tostringtag@2.0.1: @@ -1207,11 +1208,11 @@ packages: dependencies: '@next/eslint-plugin-next': 13.4.9 '@rushstack/eslint-patch': 1.3.2 - '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) eslint: 8.44.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.44.0) eslint-plugin-react: 7.32.2(eslint@8.44.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0) @@ -1240,7 +1241,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0): + /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -1250,8 +1251,8 @@ packages: debug: 4.3.4 enhanced-resolve: 5.15.0 eslint: 8.44.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) get-tsconfig: 4.6.2 globby: 13.2.2 is-core-module: 2.12.1 @@ -1264,7 +1265,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -1285,16 +1286,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) debug: 3.2.7 eslint: 8.44.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -1304,7 +1305,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 @@ -1312,14 +1313,14 @@ packages: doctrine: 2.1.0 eslint: 8.44.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) has: 1.0.3 is-core-module: 2.12.1 is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.2 - semver: 6.3.0 + semver: 6.3.1 tsconfig-paths: 3.14.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -1349,7 +1350,7 @@ packages: minimatch: 3.1.2 object.entries: 1.1.6 object.fromentries: 2.0.6 - semver: 6.3.0 + semver: 6.3.1 dev: true /eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0): @@ -1381,7 +1382,7 @@ packages: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 dev: true @@ -1406,7 +1407,7 @@ packages: regexp-tree: 0.1.27 regjsparser: 0.10.0 safe-regex: 2.1.1 - semver: 7.5.3 + semver: 7.5.4 strip-indent: 3.0.0 dev: true @@ -1641,7 +1642,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 functions-have-names: 1.2.3 dev: true @@ -2361,7 +2362,7 @@ packages: '@next/env': 13.4.9 '@swc/helpers': 0.5.1 busboy: 1.6.0 - caniuse-lite: 1.0.30001512 + caniuse-lite: 1.0.30001515 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2387,8 +2388,8 @@ packages: resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==} dev: false - /node-releases@2.0.12: - resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} dev: true /normalize-package-data@2.5.0: @@ -2396,7 +2397,7 @@ packages: dependencies: hosted-git-info: 2.8.9 resolve: 1.22.2 - semver: 5.7.1 + semver: 5.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -2458,7 +2459,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /object.fromentries@2.0.6: @@ -2467,14 +2468,14 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /object.hasown@1.1.2: resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} dependencies: define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /object.values@1.1.6: @@ -2483,7 +2484,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /ofetch@1.1.1: @@ -2657,8 +2658,8 @@ packages: engines: {node: '>= 6'} dev: true - /playwright-core@1.35.1: - resolution: {integrity: sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==} + /playwright-core@1.36.0: + resolution: {integrity: sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==} engines: {node: '>=16'} hasBin: true dev: true @@ -2759,8 +2760,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.3.0(prettier@2.8.8): - resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} + /prettier-plugin-tailwindcss@0.4.0(prettier@3.0.0): + resolution: {integrity: sha512-Rna0sDPETA0KNhMHlN8wxKNgfSa8mTl2hPPAGxnbv6tUcHT6J4RQmQ8TLXyhB7Dm5Von4iHloBxTyClYM6wT0A==} engines: {node: '>=12.17.0'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -2768,7 +2769,7 @@ packages: '@shopify/prettier-plugin-liquid': '*' '@shufo/prettier-plugin-blade': '*' '@trivago/prettier-plugin-sort-imports': '*' - prettier: '>=2.2.0' + prettier: ^2.2 || ^3.0 prettier-plugin-astro: '*' prettier-plugin-css-order: '*' prettier-plugin-import-sort: '*' @@ -2811,12 +2812,12 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - prettier: 2.8.8 + prettier: 3.0.0 dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} + /prettier@3.0.0: + resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + engines: {node: '>=14'} hasBin: true dev: true @@ -3039,18 +3040,18 @@ packages: loose-envify: 1.4.0 dev: false - /semver@5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true dev: true - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true dev: true - /semver@7.5.3: - resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} hasBin: true dependencies: @@ -3180,7 +3181,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 get-intrinsic: 1.2.1 has-symbols: 1.0.3 internal-slot: 1.0.5 @@ -3194,7 +3195,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /string.prototype.trimend@1.0.6: @@ -3202,7 +3203,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /string.prototype.trimstart@1.0.6: @@ -3210,7 +3211,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.2 + es-abstract: 1.21.3 dev: true /strip-ansi@6.0.1: @@ -3308,7 +3309,7 @@ packages: resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: - '@pkgr/utils': 2.4.1 + '@pkgr/utils': 2.4.2 tslib: 2.6.0 dev: true @@ -3443,6 +3444,17 @@ packages: engines: {node: '>=8'} dev: true + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.10 + dev: true + /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: @@ -3535,8 +3547,8 @@ packages: is-symbol: 1.0.4 dev: true - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + /which-typed-array@1.1.10: + resolution: {integrity: sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 From f21f41c59d9c4d62423059fae98aa3d659b822b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 14:14:40 +0200 Subject: [PATCH 20/57] feat(poc): fix thumbnail gallery --- components/grid/tile.tsx | 3 ++- components/product/gallery.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/components/grid/tile.tsx b/components/grid/tile.tsx index b2f219276..3b6d7c2fa 100644 --- a/components/grid/tile.tsx +++ b/components/grid/tile.tsx @@ -41,7 +41,8 @@ export function GridTileImage({ {props.src ? ( {props.title From 3d857f471efca3e540a33e0de55fbf0078b7d14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 14:15:10 +0200 Subject: [PATCH 21/57] feat(poc): use group id instead of parent id --- lib/shopware/transform.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index 3afef3a06..b9bfab8f5 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -242,9 +242,11 @@ function transformOptions(parent: ExtendedProduct): ProductOption[] { const productOptions: ProductOption[] = []; if (parent.children && parent.parentId === null && parent.children.length > 0) { const group: { [key: string]: string[] } = {}; + const groupId: { [key: string]: string } = {}; parent.children.map((child) => { child.options?.map((option) => { if (option && option.group) { + groupId[option.group.name] = option.groupId; group[option.group.name] = group[option.group.name] ? [...new Set([...(group[option.group.name] as []), ...[option.name]])] : [option.name]; @@ -252,13 +254,15 @@ function transformOptions(parent: ExtendedProduct): ProductOption[] { }); }); - if (parent.id) { - for (const [key, value] of Object.entries(group)) { - productOptions.push({ - id: parent.id, - name: key, - values: value - }); + for (const [key, value] of Object.entries(group)) { + for (const [currentGroupName, currentGroupId] of Object.entries(groupId)) { + if (key === currentGroupName) { + productOptions.push({ + id: currentGroupId, + name: key, + values: value + }); + } } } } From 245127227e9df55e0a48336c88b959ccb18d8f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 16:18:09 +0200 Subject: [PATCH 22/57] feat(poc): if parent change variant url --- lib/shopware/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 7af3cfa6e..17d568725 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -116,10 +116,33 @@ export async function getSearchCollectionProducts(params?: { const searchCriteria = { ...criteria, ...sorting }; const res = await requestSearchCollectionProducts(searchCriteria); + res.elements = await changeVariantUrlToParentUrl(res); return res ? transformProducts(res) : []; } +export async function changeVariantUrlToParentUrl( + collection: ExtendedProductListingResult +): Promise { + const newElements: ExtendedProduct[] = []; + if (collection.elements && collection.elements.length > 0) { + await Promise.all( + collection.elements.map(async (item) => { + if (item.parentId && item.seoUrls && item.seoUrls[0]) { + const parentProduct = await getFirstProduct(item.parentId); + if (parentProduct && parentProduct.seoUrls && parentProduct.seoUrls[0]) { + item.seoUrls[0].seoPathInfo = parentProduct.seoUrls[0].seoPathInfo; + } + } + + newElements.push(item); + }) + ); + } + + return newElements; +} + export async function getCollectionProducts(params?: { collection: string; page?: number; @@ -152,6 +175,7 @@ export async function getCollectionProducts(params?: { : params?.defaultSearchCriteria; const productsCriteria = { ...criteria, ...sorting }; res = await requestCategoryProductsCollection(category, productsCriteria); + res.elements = await changeVariantUrlToParentUrl(res); } return res From 85e05847823058bfb4083ec9b59292819d2f8d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 13 Jul 2023 16:46:37 +0200 Subject: [PATCH 23/57] feat(poc): improve loading style --- .../(collection)/[...collection]/page.tsx | 13 +++++++--- app/search/loading.tsx | 26 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index 0bce5ab50..7de68dae9 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -1,4 +1,3 @@ -import { getCollection, getCollectionProducts } from 'lib/shopware'; import { Metadata } from 'next'; import { notFound } from 'next/navigation'; @@ -7,6 +6,8 @@ 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 { getCollection, getCollectionProducts } from 'lib/shopware'; import { defaultSort, sorting } from 'lib/constants'; export const runtime = 'edge'; @@ -49,7 +50,7 @@ export default async function CategoryPage({ {products.length === 0 ? (

{`No products found in this collection`}

) : ( -
+
@@ -57,8 +58,12 @@ export default async function CategoryPage({ -
diff --git a/app/search/loading.tsx b/app/search/loading.tsx index 3b4bd399f..730c40184 100644 --- a/app/search/loading.tsx +++ b/app/search/loading.tsx @@ -1,13 +1,25 @@ import Grid from 'components/grid'; +import FilterList from 'components/layout/search/filter'; +import { sorting } from 'lib/constants'; export default function Loading() { return ( - - {Array(12) - .fill(0) - .map((_, index) => { - return ; - })} - +
+
+
+ + {Array(12) + .fill(0) + .map((_, index) => { + return ( + + ); + })} + +
+
+ +
+
); } From ab51c9464e091243bc193abbdbc2b21c3fa13012 Mon Sep 17 00:00:00 2001 From: mkucmus Date: Thu, 13 Jul 2023 23:09:28 +0200 Subject: [PATCH 24/57] feat: add to cart and cart sidebar --- app/product/[...handle]/page.tsx | 7 ++-- components/cart/actions.ts | 29 ++++++++------- components/cart/add-to-cart.tsx | 15 ++++---- components/cart/index.tsx | 6 ++-- components/cart/modal.tsx | 5 +-- lib/shopware/api.ts | 61 +++++++++++++++++++++++--------- lib/shopware/index.ts | 9 +++-- pnpm-lock.yaml | 28 +-------------- 8 files changed, 88 insertions(+), 72 deletions(-) diff --git a/app/product/[...handle]/page.tsx b/app/product/[...handle]/page.tsx index 573fe475b..f1ae3aa18 100644 --- a/app/product/[...handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -2,12 +2,11 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { Suspense } from 'react'; +import { AddToCart } from 'components/cart/add-to-cart'; import Grid from 'components/grid'; import Footer from 'components/layout/footer'; import ProductGridItems from 'components/layout/product-grid-items'; -import { AddToCart } from 'components/cart/add-to-cart'; import { Gallery } from 'components/product/gallery'; -import { VariantSelector } from 'components/product/variant-selector'; import Prose from 'components/prose'; import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; import { getProduct, getProductRecommendations } from 'lib/shopware'; @@ -98,13 +97,13 @@ export default async function ProductPage({ params }: { params: { handle: string
- + {/* */} {product.descriptionHtml ? ( ) : null} - +
diff --git a/components/cart/actions.ts b/components/cart/actions.ts index 5e5f2b2ea..b5a187971 100644 --- a/components/cart/actions.ts +++ b/components/cart/actions.ts @@ -1,17 +1,21 @@ 'use server'; -import { addToCart, removeFromCart, updateCart } from 'lib/shopify'; +import { requestAddToCart, requestCart } from 'lib/shopware/api'; import { cookies } from 'next/headers'; +export const fetchCart = async (cartId?: string) => { + await requestCart(cartId); +}; export const addItem = async (variantId: string | undefined): Promise => { - const cartId = cookies().get('cartId')?.value; + const cartId = cookies().get('sw-context-token')?.value || ''; - if (!cartId || !variantId) { + if (!variantId) { return new Error('Missing cartId or variantId'); } try { - await addToCart(cartId, [{ merchandiseId: variantId, quantity: 1 }]); + await requestAddToCart(variantId, cartId); } catch (e) { + console.error('eeeee', e); return new Error('Error adding item', { cause: e }); } }; @@ -23,7 +27,7 @@ export const removeItem = async (lineId: string): Promise => return new Error('Missing cartId'); } try { - await removeFromCart(cartId, [lineId]); + //await removeFromCart(cartId, [lineId]); } catch (e) { return new Error('Error removing item', { cause: e }); } @@ -44,14 +48,15 @@ export const updateItemQuantity = async ({ return new Error('Missing cartId'); } try { - await updateCart(cartId, [ - { - id: lineId, - merchandiseId: variantId, - quantity - } - ]); + // await updateCart(cartId, [ + // { + // id: lineId, + // merchandiseId: variantId, + // quantity + // } + // ]); } catch (e) { return new Error('Error updating item quantity', { cause: e }); } }; + diff --git a/components/cart/add-to-cart.tsx b/components/cart/add-to-cart.tsx index eecee848e..5362ff590 100644 --- a/components/cart/add-to-cart.tsx +++ b/components/cart/add-to-cart.tsx @@ -7,13 +7,16 @@ import { useEffect, useState, useTransition } from 'react'; import LoadingDots from 'components/loading-dots'; import { ProductVariant } from 'lib/shopify/types'; +import { Product } from 'lib/shopware/types'; export function AddToCart({ + product, variants, - availableForSale + availableForSale, }: { variants: ProductVariant[]; availableForSale: boolean; + product: Product }) { const [selectedVariantId, setSelectedVariantId] = useState(variants[0]?.id); const router = useRouter(); @@ -39,12 +42,12 @@ export function AddToCart({ onClick={() => { if (!availableForSale) return; startTransition(async () => { - const error = await addItem(selectedVariantId); + const error = await addItem(product.id); - if (error) { - alert(error); - return; - } + if (error) { + console.error(error); + return; + } router.refresh(); }); diff --git a/components/cart/index.tsx b/components/cart/index.tsx index ab2372af1..6a68685ff 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -1,11 +1,13 @@ +import { fetchCart } from 'components/cart/actions'; import { getCart } from 'lib/shopware'; import { cookies } from 'next/headers'; import CartModal from './modal'; export default async function Cart() { const cartId = cookies().get('sw-context-token')?.value; - let cartIdUpdated = true; - const cart = await getCart(); + await fetchCart(cartId); + let cartIdUpdated = false; + const cart = await getCart(cartId); if (cartId !== cart.id) { cartIdUpdated = true; diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index bb14abf2a..71deaa6a4 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -21,7 +21,7 @@ type MerchandiseSearchParams = { }; export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdUpdated: boolean }) { - const [, setCookie] = useCookies(['cartId']); + const [, setCookie] = useCookies(['sw-context-token']); const [isOpen, setIsOpen] = useState(false); const quantityRef = useRef(cart.totalQuantity); const openCart = () => setIsOpen(true); @@ -29,7 +29,7 @@ export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdU useEffect(() => { if (cartIdUpdated) { - setCookie('cartId', cart.id, { + setCookie('sw-context-token', cart.id, { path: '/', sameSite: 'strict', secure: process.env.NODE_ENV === 'production' @@ -39,6 +39,7 @@ export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdU }, [setCookie, cartIdUpdated, cart.id]); useEffect(() => { + console.warn('cart modal', cart); // Open cart modal when when quantity changes. if (cart.totalQuantity !== quantityRef.current) { // But only if it's not already open (quantity also changes when editing items in cart). diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 89c666674..ed73ee8b4 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,5 +1,6 @@ import { createAPIClient, RequestReturnType } from '@shopware/api-client'; import { operations } from '@shopware/api-client/api-types'; +import { cookies } from 'next/headers'; import { ExtendedCategory, ExtendedCriteria, @@ -19,11 +20,19 @@ import { const domainSW = `https://${process.env.SHOPWARE_STORE_DOMAIN!}/${process.env.SHOPWARE_API_TYPE!}`; const accessTokenSW = `${process.env.SHOPWARE_ACCESS_TOKEN}`; -const apiInstance = createAPIClient({ - baseURL: domainSW, - accessToken: accessTokenSW, - apiType: 'store-api' -}); +function getApiClient(cartId?: string) { + const apiInstance = createAPIClient({ + baseURL: domainSW, + accessToken: accessTokenSW, + apiType: 'store-api', + contextToken: cartId, + onContextChanged(newContextToken: string) { + //cookies().set('sw-context-token', newContextToken); + } + }); + + return apiInstance; +} // reimport operations return types to use it in application export type ApiReturnType = RequestReturnType< @@ -35,7 +44,7 @@ export async function requestNavigation( type: StoreNavigationTypeSW, depth: number ): Promise { - return await apiInstance.invoke( + return await getApiClient(cookies().get('sw-context-token')).invoke( 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls', { activeId: type, @@ -49,7 +58,7 @@ export async function requestCategory( categoryId: string, criteria?: Partial ): Promise { - return await apiInstance.invoke('readCategory post /category/{navigationId}?slots', { + return await getApiClient().invoke('readCategory post /category/{navigationId}?slots', { navigationId: categoryId, criteria }); @@ -58,20 +67,20 @@ export async function requestCategory( export async function requestCategoryList( criteria: Partial ): Promise { - return await apiInstance.invoke('readCategoryList post /category', criteria); + return await getApiClient().invoke('readCategoryList post /category', criteria); } export async function requestProductsCollection( criteria: Partial ): Promise { - return await apiInstance.invoke('readProduct post /product', criteria); + return await getApiClient().invoke('readProduct post /product', criteria); } export async function requestCategoryProductsCollection( categoryId: string, criteria: Partial ): Promise { - return await apiInstance.invoke('readProductListing post /product-listing/{categoryId}', { + return await getApiClient().invoke('readProductListing post /product-listing/{categoryId}', { ...criteria, categoryId: categoryId }); @@ -80,14 +89,14 @@ export async function requestCategoryProductsCollection( export async function requestSearchCollectionProducts( criteria?: Partial ): Promise { - return await apiInstance.invoke('searchPage post /search', { + return await getApiClient().invoke('searchPage post /search', { search: encodeURIComponent(criteria?.query || ''), ...criteria }); } export async function requestSeoUrls(routeName: RouteNames, page: number = 1, limit: number = 100) { - return await apiInstance.invoke('readSeoUrl post /seo-url', { + return await getApiClient().invoke('readSeoUrl post /seo-url', { page: page, limit: limit, filter: [ @@ -105,7 +114,7 @@ export async function requestSeoUrl( page: number = 1, limit: number = 1 ): Promise { - return await apiInstance.invoke('readSeoUrl post /seo-url', { + return await getApiClient().invoke('readSeoUrl post /seo-url', { page: page, limit: limit, filter: [ @@ -134,7 +143,7 @@ export async function requestCrossSell( productId: string, criteria?: Partial ): Promise { - return await apiInstance.invoke( + return await getApiClient().invoke( 'readProductCrossSellings post /product/{productId}/cross-selling', { productId: productId, @@ -143,6 +152,26 @@ export async function requestCrossSell( ); } -export async function requestCart() { - return apiInstance.invoke('readCart get /checkout/cart?name', {}); +export async function requestCart(cartId?: string) { + return getApiClient(cartId).invoke('readCart get /checkout/cart?name', {}); +} + +export async function requestContext(cartId?: string) { + return getApiClient(cartId).invoke('readCart get /checkout/cart?name', {}); +} + +export async function requestAddToCart(itemId: string, cartId: string) { + try { + return getApiClient(cartId).invoke('addLineItem post /checkout/cart/line-item', { + items: [ + { + referencedId: itemId, + quantity: 1, + type: 'product' + } + ] + }); + } catch (e) { + console.error('e', e); + } } \ No newline at end of file diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index b5938c8ed..a7a7fe1b4 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -217,8 +217,8 @@ export async function getProductRecommendations(productId: string): Promise { - const cartData = await requestCart(); +export async function getCart(cartId?: string): Promise { + const cartData = await requestCart(cartId); let cart: Cart = { checkoutUrl: 'https://frontends-demo.vercel.app', @@ -260,7 +260,10 @@ export async function getCart(): Promise { title: lineItem.label }, availableForSale: true, - featuredImage: (lineItem as any).cover?.url, + featuredImage: { + altText: 'Cover image of ' + lineItem.label, + url: (lineItem as any).cover?.url + }, handle: '', options: [], variants: [], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 169c48afc..dcd7e05f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ dependencies: version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@shopware/api-client': specifier: 0.0.0-canary-20230706101754 - version: 0.0.0-canary-20230706101754 + version: link:../../frontends/packages/api-client-next '@vercel/og': specifier: ^0.5.8 version: 0.5.8 @@ -379,12 +379,6 @@ packages: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: true - /@shopware/api-client@0.0.0-canary-20230706101754: - resolution: {integrity: sha512-h7nCTWVu6bLbxdKT8vEJcUVKVu/RveaZ3M1PxrQAMiP6b2nQeqRh5+QlqTEFGk+Middy11MINH70Wp6GMx+Y+A==} - dependencies: - ofetch: 1.1.1 - dev: false - /@shuding/opentype.js@1.4.0-beta.0: resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} engines: {node: '>= 8.0.0'} @@ -1046,10 +1040,6 @@ packages: engines: {node: '>=6'} dev: true - /destr@2.0.0: - resolution: {integrity: sha512-FJ9RDpf3GicEBvzI3jxc2XhHzbqD8p4ANw/1kPsFBfTvP1b7Gn/Lg1vO7R9J4IVgoMbyUmFrFGZafJ1hPZpvlg==} - dev: false - /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -2383,10 +2373,6 @@ packages: - babel-plugin-macros dev: false - /node-fetch-native@1.2.0: - resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==} - dev: false - /node-releases@2.0.12: resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} dev: true @@ -2486,14 +2472,6 @@ packages: es-abstract: 1.21.2 dev: true - /ofetch@1.1.1: - resolution: {integrity: sha512-SSMoktrp9SNLi20BWfB/BnnKcL0RDigXThD/mZBeQxkIRv1xrd9183MtLdsqRYLYSqW0eTr5t8w8MqjNhvoOQQ==} - dependencies: - destr: 2.0.0 - node-fetch-native: 1.2.0 - ufo: 1.1.2 - dev: false - /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -3457,10 +3435,6 @@ packages: hasBin: true dev: true - /ufo@1.1.2: - resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} - dev: false - /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: From d24146d45e5ef2bd78d52be0900935cbfb3c9e68 Mon Sep 17 00:00:00 2001 From: mkucmus Date: Thu, 13 Jul 2023 11:40:21 +0200 Subject: [PATCH 25/57] fix: use referencedId for item id --- lib/shopware/index.ts | 67 ++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 17d568725..437a81752 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -1,44 +1,44 @@ import { - requestCart, - requestCategory, - requestCategoryList, - requestCategoryProductsCollection, - requestCrossSell, - requestNavigation, - requestProductsCollection, - requestSearchCollectionProducts, - requestSeoUrl, - requestSeoUrls +requestCart, +requestCategory, +requestCategoryList, +requestCategoryProductsCollection, +requestCrossSell, +requestNavigation, +requestProductsCollection, +requestSearchCollectionProducts, +requestSeoUrl, +requestSeoUrls } from './api'; import { ExtendedCategory, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; import { - getDefaultCategoryCriteria, - getDefaultCategoryWithCmsCriteria, - getDefaultCrossSellingCriteria, - getDefaultProductCriteria, - getDefaultProductsCriteria, - getDefaultSearchProductsCriteria, - getDefaultSubCategoriesCriteria, - getSortingCriteria +getDefaultCategoryCriteria, +getDefaultCategoryWithCmsCriteria, +getDefaultCrossSellingCriteria, +getDefaultProductCriteria, +getDefaultProductsCriteria, +getDefaultSearchProductsCriteria, +getDefaultSubCategoriesCriteria, +getSortingCriteria } from './criteria'; import { - transformCollection, - transformHandle, - transformMenu, - transformPage, - transformProduct, - transformProducts, - transformSubCollection +transformCollection, +transformHandle, +transformMenu, +transformPage, +transformProduct, +transformProducts, +transformSubCollection } from './transform'; import { - ApiSchemas, - Cart, - CategoryListingResultSW, - Menu, - Page, - Product, - ProductListingCriteria, - StoreNavigationTypeSW +ApiSchemas, +Cart, +CategoryListingResultSW, +Menu, +Page, +Product, +ProductListingCriteria, +StoreNavigationTypeSW } from './types'; export async function getMenu(params?: { @@ -282,6 +282,7 @@ export async function getCart(): Promise { cartData.lineItems?.map((lineItem) => ({ id: lineItem.id || '', quantity: lineItem.quantity ?? 0, + id: lineItem.referencedId || '', cost: { totalAmount: { amount: (lineItem as any)?.price?.totalPrice || '', From d61acff7d15d068efeeea3ca3498581902a14c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 11:30:47 +0200 Subject: [PATCH 26/57] feat(poc): change types for cart component --- components/cart/delete-item-button.tsx | 2 +- components/cart/edit-item-quantity-button.tsx | 2 +- components/cart/modal.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/cart/delete-item-button.tsx b/components/cart/delete-item-button.tsx index 4684f5a92..395d96606 100644 --- a/components/cart/delete-item-button.tsx +++ b/components/cart/delete-item-button.tsx @@ -3,7 +3,7 @@ import LoadingDots from 'components/loading-dots'; import { useRouter } from 'next/navigation'; import clsx from 'clsx'; -import type { CartItem } from 'lib/shopify/types'; +import type { CartItem } from 'lib/shopware/types'; import { useTransition } from 'react'; import { removeItem } from 'components/cart/actions'; diff --git a/components/cart/edit-item-quantity-button.tsx b/components/cart/edit-item-quantity-button.tsx index ea17eaf92..0bef12636 100644 --- a/components/cart/edit-item-quantity-button.tsx +++ b/components/cart/edit-item-quantity-button.tsx @@ -6,7 +6,7 @@ import { removeItem, updateItemQuantity } from 'components/cart/actions'; import MinusIcon from 'components/icons/minus'; import PlusIcon from 'components/icons/plus'; import LoadingDots from 'components/loading-dots'; -import type { CartItem } from 'lib/shopify/types'; +import type { CartItem } from 'lib/shopware/types'; export default function EditItemQuantityButton({ item, diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index bb14abf2a..ae6f19d59 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -9,7 +9,7 @@ import CloseIcon from 'components/icons/close'; import ShoppingBagIcon from 'components/icons/shopping-bag'; import Price from 'components/price'; import { DEFAULT_OPTION } from 'lib/constants'; -import type { Cart } from 'lib/shopify/types'; +import type { Cart } from 'lib/shopware/types'; import { createUrl } from 'lib/utils'; import { Fragment, useEffect, useRef, useState } from 'react'; import { useCookies } from 'react-cookie'; @@ -109,7 +109,7 @@ export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdU }); const merchandiseUrl = createUrl( - `/product/${item.merchandise.product.handle}`, + `/product/${item.merchandise.product.path}`, new URLSearchParams(merchandiseSearchParams) ); From ed9d1dd2a18be204a08fd50fe10110968492bfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 11:34:31 +0200 Subject: [PATCH 27/57] feat(poc): update packages --- package.json | 14 ++--- pnpm-lock.yaml | 155 ++++++++++++++++++++++++------------------------- 2 files changed, 84 insertions(+), 85 deletions(-) diff --git a/package.json b/package.json index ed0046c91..4b7fa9153 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "private": true, "engines": { - "node": ">=16", - "pnpm": ">=7" + "node": ">=18", + "pnpm": ">=8" }, "scripts": { "build": "next build", @@ -24,7 +24,7 @@ "@vercel/og": "^0.5.8", "clsx": "^1.2.1", "is-empty-iterable": "^3.0.0", - "next": "13.4.9", + "next": "13.4.10", "react": "18.2.0", "react-cookie": "^4.1.1", "react-dom": "18.2.0", @@ -34,20 +34,20 @@ "@playwright/test": "^1.36.0", "@tailwindcss/typography": "^0.5.9", "@types/node": "^20.4.2", - "@types/react": "18.2.14", + "@types/react": "18.2.15", "@types/react-dom": "18.2.7", "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.14", "eslint": "^8.44.0", - "eslint-config-next": "^13.4.9", + "eslint-config-next": "^13.4.10", "eslint-config-prettier": "^8.8.0", "eslint-plugin-unicorn": "^47.0.0", "husky": "^8.0.3", "lint-staged": "^13.2.3", - "postcss": "^8.4.25", + "postcss": "^8.4.26", "prettier": "^3.0.0", "prettier-plugin-tailwindcss": "^0.4.0", - "tailwindcss": "^3.3.2", + "tailwindcss": "^3.3.3", "typescript": "5.1.6" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d7e59991..37061ba9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ dependencies: specifier: ^3.0.0 version: 3.0.0 next: - specifier: 13.4.9 - version: 13.4.9(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.10 + version: 13.4.10(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -42,13 +42,13 @@ devDependencies: version: 1.36.0 '@tailwindcss/typography': specifier: ^0.5.9 - version: 0.5.9(tailwindcss@3.3.2) + version: 0.5.9(tailwindcss@3.3.3) '@types/node': specifier: ^20.4.2 version: 20.4.2 '@types/react': - specifier: 18.2.14 - version: 18.2.14 + specifier: 18.2.15 + version: 18.2.15 '@types/react-dom': specifier: 18.2.7 version: 18.2.7 @@ -57,13 +57,13 @@ devDependencies: version: 1.0.0 autoprefixer: specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.25) + version: 10.4.14(postcss@8.4.26) eslint: specifier: ^8.44.0 version: 8.44.0 eslint-config-next: - specifier: ^13.4.9 - version: 13.4.9(eslint@8.44.0)(typescript@5.1.6) + specifier: ^13.4.10 + version: 13.4.10(eslint@8.44.0)(typescript@5.1.6) eslint-config-prettier: specifier: ^8.8.0 version: 8.8.0(eslint@8.44.0) @@ -77,8 +77,8 @@ devDependencies: specifier: ^13.2.3 version: 13.2.3 postcss: - specifier: ^8.4.25 - version: 8.4.25 + specifier: ^8.4.26 + version: 8.4.26 prettier: specifier: ^3.0.0 version: 3.0.0 @@ -86,8 +86,8 @@ devDependencies: specifier: ^0.4.0 version: 0.4.0(prettier@3.0.0) tailwindcss: - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.3.3 + version: 3.3.3 typescript: specifier: 5.1.6 version: 5.1.6 @@ -235,18 +235,18 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@next/env@13.4.9: - resolution: {integrity: sha512-vuDRK05BOKfmoBYLNi2cujG2jrYbEod/ubSSyqgmEx9n/W3eZaJQdRNhTfumO+qmq/QTzLurW487n/PM/fHOkw==} + /@next/env@13.4.10: + resolution: {integrity: sha512-3G1yD/XKTSLdihyDSa8JEsaWOELY+OWe08o0LUYzfuHp1zHDA8SObQlzKt+v+wrkkPcnPweoLH1ImZeUa0A1NQ==} dev: false - /@next/eslint-plugin-next@13.4.9: - resolution: {integrity: sha512-nDtGpa992tNyAkT/KmSMy7QkHfNZmGCBYhHtafU97DubqxzNdvLsqRtliQ4FU04CysRCtvP2hg8rRC1sAKUTUA==} + /@next/eslint-plugin-next@13.4.10: + resolution: {integrity: sha512-YJqyq6vk39JQfvaNtN83t/p5Jy45+bazRL+V4QI8FPd3FBqFYMEsULiwRLgSJMgFqkk4t4JbeZurz+gILEAFpA==} dependencies: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.4.9: - resolution: {integrity: sha512-TVzGHpZoVBk3iDsTOQA/R6MGmFp0+17SWXMEWd6zG30AfuELmSSMe2SdPqxwXU0gbpWkJL1KgfLzy5ReN0crqQ==} + /@next/swc-darwin-arm64@13.4.10: + resolution: {integrity: sha512-4bsdfKmmg7mgFGph0UorD1xWfZ5jZEw4kKRHYEeTK9bT1QnMbPVPlVXQRIiFPrhoDQnZUoa6duuPUJIEGLV1Jg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -254,8 +254,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.4.9: - resolution: {integrity: sha512-aSfF1fhv28N2e7vrDZ6zOQ+IIthocfaxuMWGReB5GDriF0caTqtHttAvzOMgJgXQtQx6XhyaJMozLTSEXeNN+A==} + /@next/swc-darwin-x64@13.4.10: + resolution: {integrity: sha512-ngXhUBbcZIWZWqNbQSNxQrB9T1V+wgfCzAor2olYuo/YpaL6mUYNUEgeBMhr8qwV0ARSgKaOp35lRvB7EmCRBg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -263,8 +263,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.4.9: - resolution: {integrity: sha512-JhKoX5ECzYoTVyIy/7KykeO4Z2lVKq7HGQqvAH+Ip9UFn1MOJkOnkPRB7v4nmzqAoY+Je05Aj5wNABR1N18DMg==} + /@next/swc-linux-arm64-gnu@13.4.10: + resolution: {integrity: sha512-SjCZZCOmHD4uyM75MVArSAmF5Y+IJSGroPRj2v9/jnBT36SYFTORN8Ag/lhw81W9EeexKY/CUg2e9mdebZOwsg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -272,8 +272,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.4.9: - resolution: {integrity: sha512-OOn6zZBIVkm/4j5gkPdGn4yqQt+gmXaLaSjRSO434WplV8vo2YaBNbSHaTM9wJpZTHVDYyjzuIYVEzy9/5RVZw==} + /@next/swc-linux-arm64-musl@13.4.10: + resolution: {integrity: sha512-F+VlcWijX5qteoYIOxNiBbNE8ruaWuRlcYyIRK10CugqI/BIeCDzEDyrHIHY8AWwbkTwe6GRHabMdE688Rqq4Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -281,8 +281,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.4.9: - resolution: {integrity: sha512-iA+fJXFPpW0SwGmx/pivVU+2t4zQHNOOAr5T378PfxPHY6JtjV6/0s1vlAJUdIHeVpX98CLp9k5VuKgxiRHUpg==} + /@next/swc-linux-x64-gnu@13.4.10: + resolution: {integrity: sha512-WDv1YtAV07nhfy3i1visr5p/tjiH6CeXp4wX78lzP1jI07t4PnHHG1WEDFOduXh3WT4hG6yN82EQBQHDi7hBrQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -290,8 +290,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.4.9: - resolution: {integrity: sha512-rlNf2WUtMM+GAQrZ9gMNdSapkVi3koSW3a+dmBVp42lfugWVvnyzca/xJlN48/7AGx8qu62WyO0ya1ikgOxh6A==} + /@next/swc-linux-x64-musl@13.4.10: + resolution: {integrity: sha512-zFkzqc737xr6qoBgDa3AwC7jPQzGLjDlkNmt/ljvQJ/Veri5ECdHjZCUuiTUfVjshNIIpki6FuP0RaQYK9iCRg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -299,8 +299,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.4.9: - resolution: {integrity: sha512-5T9ybSugXP77nw03vlgKZxD99AFTHaX8eT1ayKYYnGO9nmYhJjRPxcjU5FyYI+TdkQgEpIcH7p/guPLPR0EbKA==} + /@next/swc-win32-arm64-msvc@13.4.10: + resolution: {integrity: sha512-IboRS8IWz5mWfnjAdCekkl8s0B7ijpWeDwK2O8CdgZkoCDY0ZQHBSGiJ2KViAG6+BJVfLvcP+a2fh6cdyBr9QQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -308,8 +308,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.4.9: - resolution: {integrity: sha512-ojZTCt1lP2ucgpoiFgrFj07uq4CZsq4crVXpLGgQfoFq00jPKRPgesuGPaz8lg1yLfvafkU3Jd1i8snKwYR3LA==} + /@next/swc-win32-ia32-msvc@13.4.10: + resolution: {integrity: sha512-bSA+4j8jY4EEiwD/M2bol4uVEu1lBlgsGdvM+mmBm/BbqofNBfaZ2qwSbwE2OwbAmzNdVJRFRXQZ0dkjopTRaQ==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -317,8 +317,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.4.9: - resolution: {integrity: sha512-QbT03FXRNdpuL+e9pLnu+XajZdm/TtIXVYY4lA9t+9l0fLZbHXDYEKitAqxrOj37o3Vx5ufxiRAniaIebYDCgw==} + /@next/swc-win32-x64-msvc@13.4.10: + resolution: {integrity: sha512-g2+tU63yTWmcVQKDGY0MV1PjjqgZtwM4rB1oVVi/v0brdZAcrcTV+04agKzWtvWroyFz6IqtT0MoZJA7PNyLVw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -400,7 +400,7 @@ packages: tslib: 2.6.0 dev: false - /@tailwindcss/typography@0.5.9(tailwindcss@3.3.2): + /@tailwindcss/typography@0.5.9(tailwindcss@3.3.3): resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' @@ -409,7 +409,7 @@ packages: lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.3.2 + tailwindcss: 3.3.3 dev: true /@types/cookie@0.3.3: @@ -419,7 +419,7 @@ packages: /@types/hoist-non-react-statics@3.3.1: resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.15 hoist-non-react-statics: 3.3.2 dev: false @@ -441,11 +441,11 @@ packages: /@types/react-dom@18.2.7: resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.15 dev: true - /@types/react@18.2.14: - resolution: {integrity: sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==} + /@types/react@18.2.15: + resolution: {integrity: sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.3 @@ -685,7 +685,7 @@ packages: engines: {node: '>=8'} dev: true - /autoprefixer@10.4.14(postcss@8.4.25): + /autoprefixer@10.4.14(postcss@8.4.26): resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -697,7 +697,7 @@ packages: fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.25 + postcss: 8.4.26 postcss-value-parser: 4.2.0 dev: true @@ -763,7 +763,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001515 - electron-to-chromium: 1.4.459 + electron-to-chromium: 1.4.460 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true @@ -1083,8 +1083,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.459: - resolution: {integrity: sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg==} + /electron-to-chromium@1.4.460: + resolution: {integrity: sha512-kKiHnbrHME7z8E6AYaw0ehyxY5+hdaRmeUbjBO22LZMdqTYCO29EvF0T1cQ3pJ1RN5fyMcHl1Lmcsdt9WWJpJQ==} dev: true /emoji-regex@10.2.1: @@ -1197,8 +1197,8 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-next@13.4.9(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-0fLtKRR268NArpqeXXwnLgMXPvF64YESQvptVg+RMLCaijKm3FICN9Y7Jc1p2o+yrWwE4DufJXDM/Vo53D1L7g==} + /eslint-config-next@13.4.10(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-+JjcM6lQmFR5Mw0ORm9o1CR29+z/uajgSfYAPEGIBxOhTHBgCMs7ysuwi72o7LkMmA8E3N7/h09pSGZxs0s85g==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1206,7 +1206,7 @@ packages: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 13.4.9 + '@next/eslint-plugin-next': 13.4.10 '@rushstack/eslint-patch': 1.3.2 '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) eslint: 8.44.0 @@ -2341,8 +2341,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next@13.4.9(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-vtefFm/BWIi/eWOqf1GsmKG3cjKw1k3LjuefKRcL3iiLl3zWzFdPG3as6xtxrGO6gwTzzaO1ktL4oiHt/uvTjA==} + /next@13.4.10(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-4ep6aKxVTQ7rkUW2fBLhpBr/5oceCuf4KmlUpvG/aXuDTIf9mexNSpabUD6RWPspu6wiJJvozZREhXhueYO36A==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: @@ -2359,7 +2359,7 @@ packages: sass: optional: true dependencies: - '@next/env': 13.4.9 + '@next/env': 13.4.10 '@swc/helpers': 0.5.1 busboy: 1.6.0 caniuse-lite: 1.0.30001515 @@ -2370,15 +2370,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.9 - '@next/swc-darwin-x64': 13.4.9 - '@next/swc-linux-arm64-gnu': 13.4.9 - '@next/swc-linux-arm64-musl': 13.4.9 - '@next/swc-linux-x64-gnu': 13.4.9 - '@next/swc-linux-x64-musl': 13.4.9 - '@next/swc-win32-arm64-msvc': 13.4.9 - '@next/swc-win32-ia32-msvc': 13.4.9 - '@next/swc-win32-x64-msvc': 13.4.9 + '@next/swc-darwin-arm64': 13.4.10 + '@next/swc-darwin-x64': 13.4.10 + '@next/swc-linux-arm64-gnu': 13.4.10 + '@next/swc-linux-arm64-musl': 13.4.10 + '@next/swc-linux-x64-gnu': 13.4.10 + '@next/swc-linux-x64-musl': 13.4.10 + '@next/swc-win32-arm64-msvc': 13.4.10 + '@next/swc-win32-ia32-msvc': 13.4.10 + '@next/swc-win32-x64-msvc': 13.4.10 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -2669,29 +2669,29 @@ packages: engines: {node: '>=4'} dev: true - /postcss-import@15.1.0(postcss@8.4.25): + /postcss-import@15.1.0(postcss@8.4.26): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.25 + postcss: 8.4.26 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.2 dev: true - /postcss-js@4.0.1(postcss@8.4.25): + /postcss-js@4.0.1(postcss@8.4.26): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.25 + postcss: 8.4.26 dev: true - /postcss-load-config@4.0.1(postcss@8.4.25): + /postcss-load-config@4.0.1(postcss@8.4.26): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -2704,17 +2704,17 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.25 + postcss: 8.4.26 yaml: 2.3.1 dev: true - /postcss-nested@6.0.1(postcss@8.4.25): + /postcss-nested@6.0.1(postcss@8.4.26): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.25 + postcss: 8.4.26 postcss-selector-parser: 6.0.13 dev: true @@ -2746,8 +2746,8 @@ packages: source-map-js: 1.0.2 dev: false - /postcss@8.4.25: - resolution: {integrity: sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==} + /postcss@8.4.26: + resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -3313,8 +3313,8 @@ packages: tslib: 2.6.0 dev: true - /tailwindcss@3.3.2: - resolution: {integrity: sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==} + /tailwindcss@3.3.3: + resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -3332,13 +3332,12 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.25 - postcss-import: 15.1.0(postcss@8.4.25) - postcss-js: 4.0.1(postcss@8.4.25) - postcss-load-config: 4.0.1(postcss@8.4.25) - postcss-nested: 6.0.1(postcss@8.4.25) + postcss: 8.4.26 + postcss-import: 15.1.0(postcss@8.4.26) + postcss-js: 4.0.1(postcss@8.4.26) + postcss-load-config: 4.0.1(postcss@8.4.26) + postcss-nested: 6.0.1(postcss@8.4.26) postcss-selector-parser: 6.0.13 - postcss-value-parser: 4.2.0 resolve: 1.22.2 sucrase: 3.32.0 transitivePeerDependencies: From ab2959fa15344d22844d8d9d1584e36bd0079e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 11:37:42 +0200 Subject: [PATCH 28/57] feat(poc): fix cart type --- lib/shopware/index.ts | 69 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 437a81752..bd52877d8 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -1,44 +1,44 @@ import { -requestCart, -requestCategory, -requestCategoryList, -requestCategoryProductsCollection, -requestCrossSell, -requestNavigation, -requestProductsCollection, -requestSearchCollectionProducts, -requestSeoUrl, -requestSeoUrls + requestCart, + requestCategory, + requestCategoryList, + requestCategoryProductsCollection, + requestCrossSell, + requestNavigation, + requestProductsCollection, + requestSearchCollectionProducts, + requestSeoUrl, + requestSeoUrls } from './api'; import { ExtendedCategory, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; import { -getDefaultCategoryCriteria, -getDefaultCategoryWithCmsCriteria, -getDefaultCrossSellingCriteria, -getDefaultProductCriteria, -getDefaultProductsCriteria, -getDefaultSearchProductsCriteria, -getDefaultSubCategoriesCriteria, -getSortingCriteria + getDefaultCategoryCriteria, + getDefaultCategoryWithCmsCriteria, + getDefaultCrossSellingCriteria, + getDefaultProductCriteria, + getDefaultProductsCriteria, + getDefaultSearchProductsCriteria, + getDefaultSubCategoriesCriteria, + getSortingCriteria } from './criteria'; import { -transformCollection, -transformHandle, -transformMenu, -transformPage, -transformProduct, -transformProducts, -transformSubCollection + transformCollection, + transformHandle, + transformMenu, + transformPage, + transformProduct, + transformProducts, + transformSubCollection } from './transform'; import { -ApiSchemas, -Cart, -CategoryListingResultSW, -Menu, -Page, -Product, -ProductListingCriteria, -StoreNavigationTypeSW + ApiSchemas, + Cart, + CategoryListingResultSW, + Menu, + Page, + Product, + ProductListingCriteria, + StoreNavigationTypeSW } from './types'; export async function getMenu(params?: { @@ -280,9 +280,8 @@ export async function getCart(): Promise { id: cartData.token || '', lines: cartData.lineItems?.map((lineItem) => ({ - id: lineItem.id || '', - quantity: lineItem.quantity ?? 0, id: lineItem.referencedId || '', + quantity: lineItem.quantity ?? 0, cost: { totalAmount: { amount: (lineItem as any)?.price?.totalPrice || '', From 88f0016a1c1fcef122b4066fb45003edbbbafb6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 12:41:33 +0200 Subject: [PATCH 29/57] feat(poc): try fix 404 error on prod dep --- lib/shopware/api.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 89c666674..35655a275 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -123,6 +123,16 @@ export async function requestSeoUrl( type: 'equals', field: 'seoPathInfo', value: handle + }, + { + type: 'equals', + field: 'seoPathInfo', + value: decodeURI(handle) + '/' + }, + { + type: 'equals', + field: 'seoPathInfo', + value: decodeURI(handle) } ] } @@ -145,4 +155,4 @@ export async function requestCrossSell( export async function requestCart() { return apiInstance.invoke('readCart get /checkout/cart?name', {}); -} \ No newline at end of file +} From f2bf4d34b74dba039389bf51d3efb324320a6465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 12:49:26 +0200 Subject: [PATCH 30/57] feat(poc): try fix 404 error on prod dep --- lib/shopware/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 35655a275..13892765f 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -127,12 +127,12 @@ export async function requestSeoUrl( { type: 'equals', field: 'seoPathInfo', - value: decodeURI(handle) + '/' + value: decodeURIComponent(handle) + '/' }, { type: 'equals', field: 'seoPathInfo', - value: decodeURI(handle) + value: decodeURIComponent(handle) } ] } From 09ecbe89e32abd24f40069ba842917bb953d43a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 12:55:01 +0200 Subject: [PATCH 31/57] feat(poc): next dump try --- lib/shopware/api.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 13892765f..b89f33ca2 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -105,6 +105,7 @@ export async function requestSeoUrl( page: number = 1, limit: number = 1 ): Promise { + const fixedHandle = handle.replace('%2F', '/'); return await apiInstance.invoke('readSeoUrl post /seo-url', { page: page, limit: limit, @@ -117,22 +118,12 @@ export async function requestSeoUrl( { type: 'equals', field: 'seoPathInfo', - value: handle + '/' + value: fixedHandle + '/' }, { type: 'equals', field: 'seoPathInfo', - value: handle - }, - { - type: 'equals', - field: 'seoPathInfo', - value: decodeURIComponent(handle) + '/' - }, - { - type: 'equals', - field: 'seoPathInfo', - value: decodeURIComponent(handle) + value: fixedHandle } ] } From 179b92d5d8705d04c51795b5e8b7ce0aec5edf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 13:04:52 +0200 Subject: [PATCH 32/57] feat(poc): cahnge runtime for test --- app/(cms)/[...cms]/page.tsx | 8 ++----- app/(cms)/opengraph-image.tsx | 2 +- app/api/revalidate/route.ts | 2 +- app/opengraph-image.tsx | 2 +- app/page.tsx | 2 +- app/product/[...handle]/page.tsx | 2 +- .../(collection)/[...collection]/page.tsx | 2 +- app/search/(collection)/opengraph-image.tsx | 2 +- app/search/page.tsx | 23 ++++++++++--------- lib/shopware/api.ts | 5 ++-- 10 files changed, 23 insertions(+), 27 deletions(-) diff --git a/app/(cms)/[...cms]/page.tsx b/app/(cms)/[...cms]/page.tsx index b7f6d8e72..8afa7edeb 100644 --- a/app/(cms)/[...cms]/page.tsx +++ b/app/(cms)/[...cms]/page.tsx @@ -4,15 +4,11 @@ import Prose from 'components/prose'; import { getPage } from 'lib/shopware'; import { notFound } from 'next/navigation'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export const revalidate = 43200; // 12 hours in seconds -export async function generateMetadata({ - params -}: { - params: { cms: string }; -}): Promise { +export async function generateMetadata({ params }: { params: { cms: string } }): Promise { const page = await getPage(params.cms); if (!page) return notFound(); diff --git a/app/(cms)/opengraph-image.tsx b/app/(cms)/opengraph-image.tsx index db45917a7..9f954dde6 100644 --- a/app/(cms)/opengraph-image.tsx +++ b/app/(cms)/opengraph-image.tsx @@ -1,7 +1,7 @@ import OpengraphImage from 'components/opengraph-image'; import { getPage } from 'lib/shopware'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export default async function Image({ params }: { params: { page: string } }) { const page = await getPage(params.page); diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index 94ddfff9b..da250294c 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -3,7 +3,7 @@ import { revalidateTag } from 'next/cache'; import { headers } from 'next/headers'; import { NextRequest, NextResponse } from 'next/server'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; // We always need to respond with a 200 status code to Shopify, // otherwise it will continue to retry the request. diff --git a/app/opengraph-image.tsx b/app/opengraph-image.tsx index 23762cbdd..29e3f703a 100644 --- a/app/opengraph-image.tsx +++ b/app/opengraph-image.tsx @@ -1,6 +1,6 @@ import OpengraphImage from 'components/opengraph-image'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export default async function Image() { return await OpengraphImage(); diff --git a/app/page.tsx b/app/page.tsx index 5f357726a..e1bd0d998 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,7 @@ import { ThreeItemGrid } from 'components/grid/three-items'; import Footer from 'components/layout/footer'; import { Suspense } from 'react'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export const metadata = { description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopware.', diff --git a/app/product/[...handle]/page.tsx b/app/product/[...handle]/page.tsx index 573fe475b..6f7a76400 100644 --- a/app/product/[...handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -13,7 +13,7 @@ import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; import { getProduct, getProductRecommendations } from 'lib/shopware'; import { Image } from 'lib/shopware/types'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export async function generateMetadata({ params diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index 7de68dae9..c4d1950a5 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -10,7 +10,7 @@ import Pagination from 'components/collection/pagination'; import { getCollection, getCollectionProducts } from 'lib/shopware'; import { defaultSort, sorting } from 'lib/constants'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export async function generateMetadata({ params diff --git a/app/search/(collection)/opengraph-image.tsx b/app/search/(collection)/opengraph-image.tsx index 98cd40853..d1e08d1f7 100644 --- a/app/search/(collection)/opengraph-image.tsx +++ b/app/search/(collection)/opengraph-image.tsx @@ -1,7 +1,7 @@ import OpengraphImage from 'components/opengraph-image'; import { getCollection } from 'lib/shopware'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export default async function Image({ params }: { params: { collection: string } }) { const collection = await getCollection(params.collection); diff --git a/app/search/page.tsx b/app/search/page.tsx index a8940ac44..5200149ec 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -4,7 +4,7 @@ import FilterList from 'components/layout/search/filter'; import { defaultSort, sorting } from 'lib/constants'; import { getSearchCollectionProducts } from 'lib/shopware'; -export const runtime = 'edge'; +export const runtime = 'nodejs'; export const metadata = { title: 'Search', @@ -23,16 +23,17 @@ export default async function SearchPage({ const resultsText = products.length > 1 ? 'results' : 'result'; return ( - <>{searchValue && products.length === 0 ? ( -
-

- {'There are no products that match '} - "{searchValue}" -

-
- ) : null} + <> + {searchValue && products.length === 0 ? ( +
+

+ {'There are no products that match '} + "{searchValue}" +

+
+ ) : null} {products.length > 0 ? ( -
+
{searchValue ? (

@@ -40,7 +41,7 @@ export default async function SearchPage({ "{searchValue}"

) : null} -

Good place to add other suggest search terms ;)

+

Good place to add other suggest search terms ;)

diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index b89f33ca2..81985dc5f 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -105,7 +105,6 @@ export async function requestSeoUrl( page: number = 1, limit: number = 1 ): Promise { - const fixedHandle = handle.replace('%2F', '/'); return await apiInstance.invoke('readSeoUrl post /seo-url', { page: page, limit: limit, @@ -118,12 +117,12 @@ export async function requestSeoUrl( { type: 'equals', field: 'seoPathInfo', - value: fixedHandle + '/' + value: handle + '/' }, { type: 'equals', field: 'seoPathInfo', - value: fixedHandle + value: handle } ] } From 1bfbd903cc235714b8c758f9e1d688358ba4fe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 13:08:06 +0200 Subject: [PATCH 33/57] feat(poc): change runtime for test --- app/(cms)/opengraph-image.tsx | 2 +- app/opengraph-image.tsx | 2 +- app/search/(collection)/opengraph-image.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/(cms)/opengraph-image.tsx b/app/(cms)/opengraph-image.tsx index 9f954dde6..db45917a7 100644 --- a/app/(cms)/opengraph-image.tsx +++ b/app/(cms)/opengraph-image.tsx @@ -1,7 +1,7 @@ import OpengraphImage from 'components/opengraph-image'; import { getPage } from 'lib/shopware'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export default async function Image({ params }: { params: { page: string } }) { const page = await getPage(params.page); diff --git a/app/opengraph-image.tsx b/app/opengraph-image.tsx index 29e3f703a..23762cbdd 100644 --- a/app/opengraph-image.tsx +++ b/app/opengraph-image.tsx @@ -1,6 +1,6 @@ import OpengraphImage from 'components/opengraph-image'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export default async function Image() { return await OpengraphImage(); diff --git a/app/search/(collection)/opengraph-image.tsx b/app/search/(collection)/opengraph-image.tsx index d1e08d1f7..98cd40853 100644 --- a/app/search/(collection)/opengraph-image.tsx +++ b/app/search/(collection)/opengraph-image.tsx @@ -1,7 +1,7 @@ import OpengraphImage from 'components/opengraph-image'; import { getCollection } from 'lib/shopware'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export default async function Image({ params }: { params: { collection: string } }) { const collection = await getCollection(params.collection); From eacf390afae16f3863dafba061817709f9a364f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 14 Jul 2023 13:36:05 +0200 Subject: [PATCH 34/57] feat(poc): revert changes --- app/(cms)/[...cms]/page.tsx | 2 +- app/api/revalidate/route.ts | 2 +- app/page.tsx | 2 +- app/product/[...handle]/page.tsx | 2 +- app/search/(collection)/[...collection]/page.tsx | 2 +- app/search/page.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/(cms)/[...cms]/page.tsx b/app/(cms)/[...cms]/page.tsx index 8afa7edeb..0a89ab4f8 100644 --- a/app/(cms)/[...cms]/page.tsx +++ b/app/(cms)/[...cms]/page.tsx @@ -4,7 +4,7 @@ import Prose from 'components/prose'; import { getPage } from 'lib/shopware'; import { notFound } from 'next/navigation'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export const revalidate = 43200; // 12 hours in seconds diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index da250294c..94ddfff9b 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -3,7 +3,7 @@ import { revalidateTag } from 'next/cache'; import { headers } from 'next/headers'; import { NextRequest, NextResponse } from 'next/server'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; // We always need to respond with a 200 status code to Shopify, // otherwise it will continue to retry the request. diff --git a/app/page.tsx b/app/page.tsx index e1bd0d998..5f357726a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,7 @@ import { ThreeItemGrid } from 'components/grid/three-items'; import Footer from 'components/layout/footer'; import { Suspense } from 'react'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export const metadata = { description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopware.', diff --git a/app/product/[...handle]/page.tsx b/app/product/[...handle]/page.tsx index 6f7a76400..573fe475b 100644 --- a/app/product/[...handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -13,7 +13,7 @@ import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; import { getProduct, getProductRecommendations } from 'lib/shopware'; import { Image } from 'lib/shopware/types'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export async function generateMetadata({ params diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index c4d1950a5..7de68dae9 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -10,7 +10,7 @@ import Pagination from 'components/collection/pagination'; import { getCollection, getCollectionProducts } from 'lib/shopware'; import { defaultSort, sorting } from 'lib/constants'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export async function generateMetadata({ params diff --git a/app/search/page.tsx b/app/search/page.tsx index 5200149ec..13b1ac2f2 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -4,7 +4,7 @@ import FilterList from 'components/layout/search/filter'; import { defaultSort, sorting } from 'lib/constants'; import { getSearchCollectionProducts } from 'lib/shopware'; -export const runtime = 'nodejs'; +export const runtime = 'edge'; export const metadata = { title: 'Search', From 47291853e3b7b9e2d682755aaab287ca10f6fa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 08:44:21 +0200 Subject: [PATCH 35/57] feat(poc): add log for api client --- app/sitemap.ts | 4 +- components/carousel.tsx | 4 +- components/cart/index.tsx | 2 +- components/layout/footer.tsx | 31 +++-- lib/shopware/api-extended.ts | 9 +- lib/shopware/api.ts | 184 ++++++++++++++++----------- lib/shopware/types.ts | 8 +- package.json | 8 +- pnpm-lock.yaml | 238 ++++++++++++++++++++--------------- 9 files changed, 289 insertions(+), 199 deletions(-) diff --git a/app/sitemap.ts b/app/sitemap.ts index d1b685ff1..e1196555b 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -19,9 +19,7 @@ export default async function sitemap(): Promise; -} \ No newline at end of file +} diff --git a/components/layout/footer.tsx b/components/layout/footer.tsx index dc17c763d..47404d475 100644 --- a/components/layout/footer.tsx +++ b/components/layout/footer.tsx @@ -1,4 +1,5 @@ import Link from 'next/link'; +import Image from 'next/image'; import GitHubIcon from 'components/icons/github'; import LogoIcon from 'components/icons/logo'; @@ -25,12 +26,8 @@ export default async function Footer() {
{menu.map((item: Menu) => ( - < nav className="col-span-1 lg:col-span-3" key={item.title + item.type} > - { - item.type === "headline" ? ( - {item.title} - ) : null - } + ))} -
- + @@ -77,15 +80,17 @@ export default async function Footer() { className="text-black dark:text-white" >
- Shopware Composable Frontends Logo + width={40} + height={40} + >
- + ); } diff --git a/lib/shopware/api-extended.ts b/lib/shopware/api-extended.ts index 07845b473..d4915593b 100644 --- a/lib/shopware/api-extended.ts +++ b/lib/shopware/api-extended.ts @@ -57,9 +57,10 @@ export type ExtendedProductListingResult = Omit & { - products?: ExtendedProduct[]; -}[]; +> & + { + products?: ExtendedProduct[]; + }[]; export type ExtendedCategory = Omit & { children?: ExtendedCategory[]; @@ -104,7 +105,7 @@ type extendedReadProductCrossSellings = { /** Found cross sellings */ 200: { content: { - 'application/json': ExtendedCrossSellingElementCollection + 'application/json': ExtendedCrossSellingElementCollection; }; }; }; diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 81985dc5f..b77d1227d 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -34,115 +34,155 @@ export type ApiReturnType = RequestRetu export async function requestNavigation( type: StoreNavigationTypeSW, depth: number -): Promise { - return await apiInstance.invoke( - 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls', - { - activeId: type, - rootId: type, - depth: depth - } - ); +): Promise { + try { + return await apiInstance.invoke( + 'readNavigation post /navigation/{activeId}/{rootId} sw-include-seo-urls', + { + activeId: type, + rootId: type, + depth: depth + } + ); + } catch (error) { + console.log(error); + } } export async function requestCategory( categoryId: string, criteria?: Partial -): Promise { - return await apiInstance.invoke('readCategory post /category/{navigationId}?slots', { - navigationId: categoryId, - criteria - }); +): Promise { + try { + return await apiInstance.invoke('readCategory post /category/{navigationId}?slots', { + navigationId: categoryId, + criteria + }); + } catch (error) { + console.log(error); + } } export async function requestCategoryList( criteria: Partial -): Promise { - return await apiInstance.invoke('readCategoryList post /category', criteria); +): Promise { + try { + return await apiInstance.invoke('readCategoryList post /category', criteria); + } catch (error) { + console.log(error); + } } export async function requestProductsCollection( criteria: Partial -): Promise { - return await apiInstance.invoke('readProduct post /product', criteria); +): Promise { + try { + return await apiInstance.invoke('readProduct post /product', criteria); + } catch (error) { + console.log(error); + } } export async function requestCategoryProductsCollection( categoryId: string, criteria: Partial -): Promise { - return await apiInstance.invoke('readProductListing post /product-listing/{categoryId}', { - ...criteria, - categoryId: categoryId - }); +): Promise { + try { + return await apiInstance.invoke('readProductListing post /product-listing/{categoryId}', { + ...criteria, + categoryId: categoryId + }); + } catch (error) { + console.log(error); + } } export async function requestSearchCollectionProducts( criteria?: Partial -): Promise { - return await apiInstance.invoke('searchPage post /search', { - search: encodeURIComponent(criteria?.query || ''), - ...criteria - }); +): Promise { + try { + return await apiInstance.invoke('searchPage post /search', { + search: encodeURIComponent(criteria?.query || ''), + ...criteria + }); + } catch (error) { + console.log(error); + } } export async function requestSeoUrls(routeName: RouteNames, page: number = 1, limit: number = 100) { - return await apiInstance.invoke('readSeoUrl post /seo-url', { - page: page, - limit: limit, - filter: [ - { - type: 'equals', - field: 'routeName', - value: routeName - } - ] - }); + try { + return await apiInstance.invoke('readSeoUrl post /seo-url', { + page: page, + limit: limit, + filter: [ + { + type: 'equals', + field: 'routeName', + value: routeName + } + ] + }); + } catch (error) { + console.log(error); + } } export async function requestSeoUrl( handle: string, page: number = 1, limit: number = 1 -): Promise { - return await apiInstance.invoke('readSeoUrl post /seo-url', { - page: page, - limit: limit, - filter: [ - { - type: 'multi', - // @ts-ignore - operator: 'or', - queries: [ - { - type: 'equals', - field: 'seoPathInfo', - value: handle + '/' - }, - { - type: 'equals', - field: 'seoPathInfo', - value: handle - } - ] - } - ] - }); +): Promise { + try { + return await apiInstance.invoke('readSeoUrl post /seo-url', { + page: page, + limit: limit, + filter: [ + { + type: 'multi', + // @ts-ignore + operator: 'or', + queries: [ + { + type: 'equals', + field: 'seoPathInfo', + value: handle + '/' + }, + { + type: 'equals', + field: 'seoPathInfo', + value: handle + } + ] + } + ] + }); + } catch (error) { + console.log(error); + } } export async function requestCrossSell( productId: string, criteria?: Partial -): Promise { - return await apiInstance.invoke( - 'readProductCrossSellings post /product/{productId}/cross-selling', - { - productId: productId, - ...criteria - } - ); +): Promise { + try { + return await apiInstance.invoke( + 'readProductCrossSellings post /product/{productId}/cross-selling', + { + productId: productId, + ...criteria + } + ); + } catch (error) { + console.log(error); + } } export async function requestCart() { - return apiInstance.invoke('readCart get /checkout/cart?name', {}); + try { + return apiInstance.invoke('readCart get /checkout/cart?name', {}); + } catch (error) { + console.log(error); + } } diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts index 4e4331d80..d91f58b77 100644 --- a/lib/shopware/types.ts +++ b/lib/shopware/types.ts @@ -10,7 +10,11 @@ export type ProductListingCriteria = { query: string; } & Omit & ExtendedCriteria; -export type RouteNames = 'frontend.navigation.page' | 'frontend.detail.page' | 'frontend.account.customer-group-registration.page' | 'frontend.landing.page' +export type RouteNames = + | 'frontend.navigation.page' + | 'frontend.detail.page' + | 'frontend.account.customer-group-registration.page' + | 'frontend.landing.page'; /** Return Types */ export type CategoryListingResultSW = { @@ -127,4 +131,4 @@ export type CartItem = { }[]; product: Product; }; -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 4b7fa9153..e58c6ff57 100644 --- a/package.json +++ b/package.json @@ -31,22 +31,22 @@ "react-paginate": "^8.2.0" }, "devDependencies": { - "@playwright/test": "^1.36.0", + "@playwright/test": "^1.36.1", "@tailwindcss/typography": "^0.5.9", "@types/node": "^20.4.2", "@types/react": "18.2.15", "@types/react-dom": "18.2.7", "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.14", - "eslint": "^8.44.0", + "eslint": "^8.45.0", "eslint-config-next": "^13.4.10", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-unicorn": "^47.0.0", + "eslint-plugin-unicorn": "^48.0.0", "husky": "^8.0.3", "lint-staged": "^13.2.3", "postcss": "^8.4.26", "prettier": "^3.0.0", - "prettier-plugin-tailwindcss": "^0.4.0", + "prettier-plugin-tailwindcss": "^0.4.1", "tailwindcss": "^3.3.3", "typescript": "5.1.6" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37061ba9f..e114d524b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,8 +38,8 @@ dependencies: devDependencies: '@playwright/test': - specifier: ^1.36.0 - version: 1.36.0 + specifier: ^1.36.1 + version: 1.36.1 '@tailwindcss/typography': specifier: ^0.5.9 version: 0.5.9(tailwindcss@3.3.3) @@ -59,17 +59,17 @@ devDependencies: specifier: ^10.4.14 version: 10.4.14(postcss@8.4.26) eslint: - specifier: ^8.44.0 - version: 8.44.0 + specifier: ^8.45.0 + version: 8.45.0 eslint-config-next: specifier: ^13.4.10 - version: 13.4.10(eslint@8.44.0)(typescript@5.1.6) + version: 13.4.10(eslint@8.45.0)(typescript@5.1.6) eslint-config-prettier: specifier: ^8.8.0 - version: 8.8.0(eslint@8.44.0) + version: 8.8.0(eslint@8.45.0) eslint-plugin-unicorn: - specifier: ^47.0.0 - version: 47.0.0(eslint@8.44.0) + specifier: ^48.0.0 + version: 48.0.0(eslint@8.45.0) husky: specifier: ^8.0.3 version: 8.0.3 @@ -83,8 +83,8 @@ devDependencies: specifier: ^3.0.0 version: 3.0.0 prettier-plugin-tailwindcss: - specifier: ^0.4.0 - version: 0.4.0(prettier@3.0.0) + specifier: ^0.4.1 + version: 0.4.1(prettier@3.0.0) tailwindcss: specifier: ^3.3.3 version: 3.3.3 @@ -132,13 +132,13 @@ packages: regenerator-runtime: 0.13.11 dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.44.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.45.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.44.0 + eslint: 8.45.0 eslint-visitor-keys: 3.4.1 dev: true @@ -153,7 +153,7 @@ packages: dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.6.0 + espree: 9.6.1 globals: 13.20.0 ignore: 5.2.4 import-fresh: 3.3.0 @@ -359,13 +359,13 @@ packages: tslib: 2.6.0 dev: true - /@playwright/test@1.36.0: - resolution: {integrity: sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==} + /@playwright/test@1.36.1: + resolution: {integrity: sha512-YK7yGWK0N3C2QInPU6iaf/L3N95dlGdbsezLya4n0ZCh3IL7VgPGxC6Gnznh9ApWdOmkJeleT2kMTcWPRZvzqg==} engines: {node: '>=16'} hasBin: true dependencies: '@types/node': 20.4.2 - playwright-core: 1.36.0 + playwright-core: 1.36.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -454,7 +454,7 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - /@typescript-eslint/parser@5.62.0(eslint@8.44.0)(typescript@5.1.6): + /@typescript-eslint/parser@5.62.0(eslint@8.45.0)(typescript@5.1.6): resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -468,7 +468,7 @@ packages: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) debug: 4.3.4 - eslint: 8.44.0 + eslint: 8.45.0 typescript: 5.1.6 transitivePeerDependencies: - supports-color @@ -636,7 +636,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 get-intrinsic: 1.2.1 is-string: 1.0.7 dev: true @@ -652,7 +652,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 dev: true @@ -662,7 +662,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 dev: true @@ -671,11 +671,23 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 dev: true + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + /ast-types-flow@0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true @@ -693,7 +705,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.9 - caniuse-lite: 1.0.30001515 + caniuse-lite: 1.0.30001516 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -762,8 +774,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001515 - electron-to-chromium: 1.4.460 + caniuse-lite: 1.0.30001516 + electron-to-chromium: 1.4.461 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true @@ -808,8 +820,8 @@ packages: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: false - /caniuse-lite@1.0.30001515: - resolution: {integrity: sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==} + /caniuse-lite@1.0.30001516: + resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==} /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1083,8 +1095,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.460: - resolution: {integrity: sha512-kKiHnbrHME7z8E6AYaw0ehyxY5+hdaRmeUbjBO22LZMdqTYCO29EvF0T1cQ3pJ1RN5fyMcHl1Lmcsdt9WWJpJQ==} + /electron-to-chromium@1.4.461: + resolution: {integrity: sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==} dev: true /emoji-regex@10.2.1: @@ -1113,11 +1125,12 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.21.3: - resolution: {integrity: sha512-ZU4miiY1j3sGPFLJ34VJXEqhpmL+HGByCinGHv4HC+Fxl2fI2Z4yR6tl0mORnDr6PA8eihWo4LmSWDbvhALckg==} + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 available-typed-arrays: 1.0.5 call-bind: 1.0.2 es-set-tostringtag: 2.0.1 @@ -1144,10 +1157,13 @@ packages: object-keys: 1.1.1 object.assign: 4.1.4 regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 safe-regex-test: 1.0.0 string.prototype.trim: 1.2.7 string.prototype.trimend: 1.0.6 string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 typed-array-byte-offset: 1.0.0 typed-array-length: 1.0.4 unbox-primitive: 1.0.2 @@ -1197,7 +1213,7 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-next@13.4.10(eslint@8.44.0)(typescript@5.1.6): + /eslint-config-next@13.4.10(eslint@8.45.0)(typescript@5.1.6): resolution: {integrity: sha512-+JjcM6lQmFR5Mw0ORm9o1CR29+z/uajgSfYAPEGIBxOhTHBgCMs7ysuwi72o7LkMmA8E3N7/h09pSGZxs0s85g==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 @@ -1208,27 +1224,27 @@ packages: dependencies: '@next/eslint-plugin-next': 13.4.10 '@rushstack/eslint-patch': 1.3.2 - '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) - eslint: 8.44.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) + eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.44.0) - eslint-plugin-react: 7.32.2(eslint@8.44.0) - eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-jsx-a11y: 6.7.1(eslint@8.45.0) + eslint-plugin-react: 7.32.2(eslint@8.45.0) + eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.45.0) typescript: 5.1.6 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color dev: true - /eslint-config-prettier@8.8.0(eslint@8.44.0): + /eslint-config-prettier@8.8.0(eslint@8.45.0): resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.44.0 + eslint: 8.45.0 dev: true /eslint-import-resolver-node@0.3.7: @@ -1241,7 +1257,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0): + /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -1250,9 +1266,9 @@ packages: dependencies: debug: 4.3.4 enhanced-resolve: 5.15.0 - eslint: 8.44.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint: 8.45.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) get-tsconfig: 4.6.2 globby: 13.2.2 is-core-module: 2.12.1 @@ -1265,7 +1281,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -1286,16 +1302,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) debug: 3.2.7 - eslint: 8.44.0 + eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.44.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -1305,15 +1321,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.44.0 + eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.44.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) has: 1.0.3 is-core-module: 2.12.1 is-glob: 4.0.3 @@ -1328,7 +1344,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.44.0): + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.45.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} peerDependencies: @@ -1343,7 +1359,7 @@ packages: axobject-query: 3.2.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.44.0 + eslint: 8.45.0 has: 1.0.3 jsx-ast-utils: 3.3.4 language-tags: 1.0.5 @@ -1353,16 +1369,16 @@ packages: semver: 6.3.1 dev: true - /eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@8.44.0): + /eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@8.45.0): resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.44.0 + eslint: 8.45.0 dev: true - /eslint-plugin-react@7.32.2(eslint@8.44.0): + /eslint-plugin-react@7.32.2(eslint@8.45.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} peerDependencies: @@ -1372,7 +1388,7 @@ packages: array.prototype.flatmap: 1.3.1 array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 - eslint: 8.44.0 + eslint: 8.45.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.4 minimatch: 3.1.2 @@ -1386,17 +1402,17 @@ packages: string.prototype.matchall: 4.0.8 dev: true - /eslint-plugin-unicorn@47.0.0(eslint@8.44.0): - resolution: {integrity: sha512-ivB3bKk7fDIeWOUmmMm9o3Ax9zbMz1Bsza/R2qm46ufw4T6VBFBaJIR1uN3pCKSmSXm8/9Nri8V+iUut1NhQGA==} + /eslint-plugin-unicorn@48.0.0(eslint@8.45.0): + resolution: {integrity: sha512-8fk/v3p1ro34JSVDBEmtOq6EEQRpMR0iTir79q69KnXFZ6DJyPkT3RAi+ZoTqhQMdDSpGh8BGR68ne1sP5cnAA==} engines: {node: '>=16'} peerDependencies: - eslint: '>=8.38.0' + eslint: '>=8.44.0' dependencies: '@babel/helper-validator-identifier': 7.22.5 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) ci-info: 3.8.0 clean-regexp: 1.0.0 - eslint: 8.44.0 + eslint: 8.45.0 esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -1406,13 +1422,12 @@ packages: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - safe-regex: 2.1.1 semver: 7.5.4 strip-indent: 3.0.0 dev: true - /eslint-scope@7.2.0: - resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} + /eslint-scope@7.2.1: + resolution: {integrity: sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: esrecurse: 4.3.0 @@ -1424,12 +1439,12 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.44.0: - resolution: {integrity: sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==} + /eslint@8.45.0: + resolution: {integrity: sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) '@eslint-community/regexpp': 4.5.1 '@eslint/eslintrc': 2.1.0 '@eslint/js': 8.44.0 @@ -1442,9 +1457,9 @@ packages: debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.0 + eslint-scope: 7.2.1 eslint-visitor-keys: 3.4.1 - espree: 9.6.0 + espree: 9.6.1 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -1454,7 +1469,6 @@ packages: globals: 13.20.0 graphemer: 1.4.0 ignore: 5.2.4 - import-fresh: 3.3.0 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -1466,14 +1480,13 @@ packages: natural-compare: 1.4.0 optionator: 0.9.3 strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 text-table: 0.2.0 transitivePeerDependencies: - supports-color dev: true - /espree@9.6.0: - resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.10.0 @@ -1642,7 +1655,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 functions-have-names: 1.2.3 dev: true @@ -2086,6 +2099,10 @@ packages: is-docker: 2.2.1 dev: true + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -2362,7 +2379,7 @@ packages: '@next/env': 13.4.10 '@swc/helpers': 0.5.1 busboy: 1.6.0 - caniuse-lite: 1.0.30001515 + caniuse-lite: 1.0.30001516 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2459,7 +2476,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /object.fromentries@2.0.6: @@ -2468,14 +2485,14 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /object.hasown@1.1.2: resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} dependencies: define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /object.values@1.1.6: @@ -2484,7 +2501,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /ofetch@1.1.1: @@ -2658,8 +2675,8 @@ packages: engines: {node: '>= 6'} dev: true - /playwright-core@1.36.0: - resolution: {integrity: sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==} + /playwright-core@1.36.1: + resolution: {integrity: sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg==} engines: {node: '>=16'} hasBin: true dev: true @@ -2760,8 +2777,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.4.0(prettier@3.0.0): - resolution: {integrity: sha512-Rna0sDPETA0KNhMHlN8wxKNgfSa8mTl2hPPAGxnbv6tUcHT6J4RQmQ8TLXyhB7Dm5Von4iHloBxTyClYM6wT0A==} + /prettier-plugin-tailwindcss@0.4.1(prettier@3.0.0): + resolution: {integrity: sha512-hwn2EiJmv8M+AW4YDkbjJ6HlZCTzLyz1QlySn9sMuKV/Px0fjwldlB7tol8GzdgqtkdPtzT3iJ4UzdnYXP25Ag==} engines: {node: '>=12.17.0'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -3004,6 +3021,16 @@ packages: tslib: 2.6.0 dev: true + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: @@ -3012,12 +3039,6 @@ packages: is-regex: 1.1.4 dev: true - /safe-regex@2.1.1: - resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} - dependencies: - regexp-tree: 0.1.27 - dev: true - /satori@0.10.1: resolution: {integrity: sha512-F4bTCkDp931tLb7+UCNPBuSQwXhikrUkI4fBQo6fA8lF0Evqqgg3nDyUpRktQpR5Ry1DIiIVqLyEwkAms87ykg==} engines: {node: '>=16'} @@ -3181,7 +3202,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 get-intrinsic: 1.2.1 has-symbols: 1.0.3 internal-slot: 1.0.5 @@ -3195,7 +3216,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /string.prototype.trimend@1.0.6: @@ -3203,7 +3224,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /string.prototype.trimstart@1.0.6: @@ -3211,7 +3232,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - es-abstract: 1.21.3 + es-abstract: 1.22.1 dev: true /strip-ansi@6.0.1: @@ -3272,8 +3293,8 @@ packages: react: 18.2.0 dev: false - /sucrase@3.32.0: - resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} + /sucrase@3.33.0: + resolution: {integrity: sha512-ARGC7vbufOHfpvyGcZZXFaXCMZ9A4fffOGC5ucOW7+WHDGlAe8LJdf3Jts1sWhDeiI1RSWrKy5Hodl+JWGdW2A==} engines: {node: '>=8'} hasBin: true dependencies: @@ -3339,7 +3360,7 @@ packages: postcss-nested: 6.0.1(postcss@8.4.26) postcss-selector-parser: 6.0.13 resolve: 1.22.2 - sucrase: 3.32.0 + sucrase: 3.33.0 transitivePeerDependencies: - ts-node dev: true @@ -3443,6 +3464,25 @@ packages: engines: {node: '>=8'} dev: true + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.10 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.10 + dev: true + /typed-array-byte-offset@1.0.0: resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} engines: {node: '>= 0.4'} From 67e5c46b9b74dd4d1b897717d7f04fb58cdc660d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 08:53:24 +0200 Subject: [PATCH 36/57] feat(poc): add log for api client --- components/cart/index.tsx | 4 + lib/shopware/index.ts | 198 ++++++++++++++++++++------------------ 2 files changed, 108 insertions(+), 94 deletions(-) diff --git a/components/cart/index.tsx b/components/cart/index.tsx index 2b1add5d9..bbb760d47 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -7,6 +7,10 @@ export default async function Cart() { let cartIdUpdated = true; const cart = await getCart(); + if (!cart) { + return null; + } + if (cartId !== cart.id) { cartIdUpdated = true; } diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index bd52877d8..40c5982e9 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -73,17 +73,19 @@ export async function getPage(handle: string | []): Promise { export async function getFirstSeoUrlElement( handle: string ): Promise { - const resSeoUrl = await requestSeoUrl(handle); - if (resSeoUrl.elements && resSeoUrl.elements.length > 0 && resSeoUrl.elements[0]) { - return resSeoUrl.elements[0]; + const seoURL = await requestSeoUrl(handle); + if (seoURL && seoURL.elements && seoURL.elements.length > 0 && seoURL.elements[0]) { + return seoURL.elements[0]; } } export async function getFirstProduct(productId: string): Promise { const productCriteria = getDefaultProductCriteria(productId); - const res: ExtendedProductListingResult = await requestProductsCollection(productCriteria); - if (res.elements && res.elements.length > 0 && res.elements[0]) { - return res.elements[0]; + const listing: ExtendedProductListingResult | undefined = await requestProductsCollection( + productCriteria + ); + if (listing && listing.elements && listing.elements.length > 0 && listing.elements[0]) { + return listing.elements[0]; } } @@ -115,10 +117,12 @@ export async function getSearchCollectionProducts(params?: { const sorting = getSortingCriteria(params?.sortKey, params?.reverse); const searchCriteria = { ...criteria, ...sorting }; - const res = await requestSearchCollectionProducts(searchCriteria); - res.elements = await changeVariantUrlToParentUrl(res); + const search = await requestSearchCollectionProducts(searchCriteria); + if (search) { + search.elements = await changeVariantUrlToParentUrl(search); + } - return res ? transformProducts(res) : []; + return search ? transformProducts(search) : []; } export async function changeVariantUrlToParentUrl( @@ -151,7 +155,7 @@ export async function getCollectionProducts(params?: { categoryId?: string; defaultSearchCriteria?: Partial; }): Promise<{ products: Product[]; total: number; limit: number }> { - let res; + let products; let category = params?.categoryId; const collectionName = transformHandle(params?.collection ?? ''); const sorting = getSortingCriteria(params?.sortKey, params?.reverse); @@ -174,23 +178,27 @@ export async function getCollectionProducts(params?: { ? getDefaultProductsCriteria(params?.page) : params?.defaultSearchCriteria; const productsCriteria = { ...criteria, ...sorting }; - res = await requestCategoryProductsCollection(category, productsCriteria); - res.elements = await changeVariantUrlToParentUrl(res); + products = await requestCategoryProductsCollection(category, productsCriteria); + if (products) { + products.elements = await changeVariantUrlToParentUrl(products); + } } - return res - ? { products: transformProducts(res), total: res.total ?? 0, limit: res.limit ?? 0 } + return products + ? { + products: transformProducts(products), + total: products.total ?? 0, + limit: products.limit ?? 0 + } : { products: [], total: 0, limit: 0 }; } export async function getCategory( seoUrl: ApiSchemas['SeoUrl'], cms: boolean = false -): Promise { +): Promise { const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria(); - const resCategory = await requestCategory(seoUrl.foreignKey, criteria); - - return resCategory; + return await requestCategory(seoUrl.foreignKey, criteria); } // This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx @@ -198,23 +206,25 @@ export async function getCollection(handle: string | []) { const collectionName = transformHandle(handle); const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { - const resCategory = await getCategory(seoUrlElement); + const category = await getCategory(seoUrlElement); const path = seoUrlElement.seoPathInfo ?? ''; - const collection = transformCollection(seoUrlElement, resCategory); + if (category) { + const collection = transformCollection(seoUrlElement, category); - return { - ...collection, - path: `/search/${path}` - }; + return { + ...collection, + path: `/search/${path}` + }; + } } } export async function getProductSeoUrls() { const productSeoUrls: { path: string; updatedAt: string }[] = []; - const res = await requestSeoUrls('frontend.detail.page'); + const seoUrls = await requestSeoUrls('frontend.detail.page'); - if (res.elements && res.elements.length > 0) { - res.elements.map((item) => + if (seoUrls && seoUrls.elements && seoUrls.elements.length > 0) { + seoUrls.elements.map((item) => productSeoUrls.push({ path: item.seoPathInfo, updatedAt: item.updatedAt ?? item.createdAt }) ); } @@ -257,74 +267,74 @@ export async function getProductRecommendations(productId: string): Promise { +export async function getCart(): Promise { const cartData = await requestCart(); - - // @ToDo: should be moved to transformCart function - const cart: Cart = { - checkoutUrl: 'https://frontends-demo.vercel.app', - cost: { - subtotalAmount: { - amount: cartData.price?.positionPrice?.toString() || '0', - currencyCode: 'EUR' - }, - totalAmount: { - amount: cartData.price?.totalPrice?.toString() || '0', - currencyCode: 'EUR' - }, - totalTaxAmount: { - amount: '0', - currencyCode: 'EUR' - } - }, - id: cartData.token || '', - lines: - cartData.lineItems?.map((lineItem) => ({ - id: lineItem.referencedId || '', - quantity: lineItem.quantity ?? 0, - cost: { - totalAmount: { - amount: (lineItem as any)?.price?.totalPrice || '', - currencyCode: 'EUR' - } + if (cartData) { + // @ToDo: should be moved to transformCart function + const cart: Cart = { + checkoutUrl: 'https://frontends-demo.vercel.app', + cost: { + subtotalAmount: { + amount: cartData.price?.positionPrice?.toString() || '0', + currencyCode: 'EUR' }, - merchandise: { - id: lineItem.referencedId ?? '', - title: lineItem.label ?? '', - selectedOptions: [], - product: { - description: lineItem.description ?? '', - descriptionHtml: lineItem.description ?? '', - id: lineItem.referencedId ?? '', - images: [], - path: '', - seo: { - description: lineItem.description ?? '', - title: lineItem.label ?? '' - }, - availableForSale: true, - featuredImage: (lineItem as any).cover?.url, - handle: '', - options: [], - variants: [], - priceRange: { - minVariantPrice: { - amount: '', // @ToDo: should be correct value - currencyCode: 'EUR' - }, - maxVariantPrice: { - amount: '', // @ToDo: should be correct value - currencyCode: 'EUR' - } - }, - tags: [], - title: lineItem.label ?? '', - updatedAt: (lineItem as any)?.payload?.updatedAt - } + totalAmount: { + amount: cartData.price?.totalPrice?.toString() || '0', + currencyCode: 'EUR' + }, + totalTaxAmount: { + amount: '0', + currencyCode: 'EUR' } - })) || [], - totalQuantity: cartData.lineItems?.length || 0 - }; - - return cart; + }, + id: cartData.token || '', + lines: + cartData.lineItems?.map((lineItem) => ({ + id: lineItem.referencedId || '', + quantity: lineItem.quantity ?? 0, + cost: { + totalAmount: { + amount: (lineItem as any)?.price?.totalPrice || '', + currencyCode: 'EUR' + } + }, + merchandise: { + id: lineItem.referencedId ?? '', + title: lineItem.label ?? '', + selectedOptions: [], + product: { + description: lineItem.description ?? '', + descriptionHtml: lineItem.description ?? '', + id: lineItem.referencedId ?? '', + images: [], + path: '', + seo: { + description: lineItem.description ?? '', + title: lineItem.label ?? '' + }, + availableForSale: true, + featuredImage: (lineItem as any).cover?.url, + handle: '', + options: [], + variants: [], + priceRange: { + minVariantPrice: { + amount: '', // @ToDo: should be correct value + currencyCode: 'EUR' + }, + maxVariantPrice: { + amount: '', // @ToDo: should be correct value + currencyCode: 'EUR' + } + }, + tags: [], + title: lineItem.label ?? '', + updatedAt: (lineItem as any)?.payload?.updatedAt + } + } + })) || [], + totalQuantity: cartData.lineItems?.length || 0 + }; + return cart; + } } From 6e09bf12c4c847d1bd54938189f3b6d059edd617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 09:10:09 +0200 Subject: [PATCH 37/57] feat(poc): remove router from layout navbar --- components/layout/navbar/search.tsx | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/components/layout/navbar/search.tsx b/components/layout/navbar/search.tsx index fff895f43..c11aa2eff 100644 --- a/components/layout/navbar/search.tsx +++ b/components/layout/navbar/search.tsx @@ -1,28 +1,29 @@ 'use client'; -import { useRouter, useSearchParams } from 'next/navigation'; +// import { useRouter, useSearchParams } from 'next/navigation'; import SearchIcon from 'components/icons/search'; -import { createUrl } from 'lib/utils'; +// import { createUrl } from 'lib/utils'; export default function Search() { - const router = useRouter(); - const searchParams = useSearchParams(); + //const router = useRouter(); + //const searchParams = useSearchParams(); function onSubmit(e: React.FormEvent) { e.preventDefault(); const val = e.target as HTMLFormElement; const search = val.search as HTMLInputElement; - const newParams = new URLSearchParams(searchParams.toString()); + console.log(`Search:` + search); + // const newParams = new URLSearchParams(searchParams.toString()); - if (search.value) { - newParams.set('q', search.value); - } else { - newParams.delete('q'); - } + // if (search.value) { + // newParams.set('q', search.value); + // } else { + // newParams.delete('q'); + // } - router.push(createUrl('/search', newParams)); + // router.push(createUrl('/search', newParams)); } return ( @@ -35,7 +36,7 @@ export default function Search() { name="search" placeholder="Search for products..." autoComplete="off" - defaultValue={searchParams?.get('q') || ''} + // defaultValue={searchParams?.get('q') || ''} className="w-full px-4 py-2 text-black dark:bg-black dark:text-gray-100" />
From ea259358b2b21bf08e665ab29759a1767fa0d5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 09:22:11 +0200 Subject: [PATCH 38/57] feat(poc): revert and change filter --- components/layout/navbar/search.tsx | 24 ++++++++++++------------ lib/shopware/api.ts | 24 +++++++----------------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/components/layout/navbar/search.tsx b/components/layout/navbar/search.tsx index c11aa2eff..bf4d12230 100644 --- a/components/layout/navbar/search.tsx +++ b/components/layout/navbar/search.tsx @@ -1,13 +1,13 @@ 'use client'; -// import { useRouter, useSearchParams } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import SearchIcon from 'components/icons/search'; -// import { createUrl } from 'lib/utils'; +import { createUrl } from 'lib/utils'; export default function Search() { - //const router = useRouter(); - //const searchParams = useSearchParams(); + const router = useRouter(); + const searchParams = useSearchParams(); function onSubmit(e: React.FormEvent) { e.preventDefault(); @@ -15,15 +15,15 @@ export default function Search() { const val = e.target as HTMLFormElement; const search = val.search as HTMLInputElement; console.log(`Search:` + search); - // const newParams = new URLSearchParams(searchParams.toString()); + const newParams = new URLSearchParams(searchParams.toString()); - // if (search.value) { - // newParams.set('q', search.value); - // } else { - // newParams.delete('q'); - // } + if (search.value) { + newParams.set('q', search.value); + } else { + newParams.delete('q'); + } - // router.push(createUrl('/search', newParams)); + router.push(createUrl('/search', newParams)); } return ( @@ -36,7 +36,7 @@ export default function Search() { name="search" placeholder="Search for products..." autoComplete="off" - // defaultValue={searchParams?.get('q') || ''} + defaultValue={searchParams?.get('q') || ''} className="w-full px-4 py-2 text-black dark:bg-black dark:text-gray-100" />
diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index b77d1227d..98f0d32ec 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -134,29 +134,19 @@ export async function requestSeoUrl( limit: number = 1 ): Promise { try { - return await apiInstance.invoke('readSeoUrl post /seo-url', { + const criteria = { page: page, limit: limit, filter: [ { - type: 'multi', - // @ts-ignore - operator: 'or', - queries: [ - { - type: 'equals', - field: 'seoPathInfo', - value: handle + '/' - }, - { - type: 'equals', - field: 'seoPathInfo', - value: handle - } - ] + type: 'contains', + field: 'seoPathInfo', + value: handle } ] - }); + }; + console.log(criteria); + return await apiInstance.invoke('readSeoUrl post /seo-url', criteria); } catch (error) { console.log(error); } From cbf9e7fd38c9a2f3325d599382e8db73e5ebb3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 11:01:12 +0200 Subject: [PATCH 39/57] feat(poc): change log to error --- lib/shopware/api.ts | 73 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 98f0d32ec..cfa4f9c3a 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,4 +1,4 @@ -import { createAPIClient, RequestReturnType } from '@shopware/api-client'; +import { createAPIClient, RequestReturnType, ApiClientError } from '@shopware/api-client'; import { operations } from '@shopware/api-client/api-types'; import { ExtendedCategory, @@ -45,7 +45,12 @@ export async function requestNavigation( } ); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -59,7 +64,12 @@ export async function requestCategory( criteria }); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -69,7 +79,12 @@ export async function requestCategoryList( try { return await apiInstance.invoke('readCategoryList post /category', criteria); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -79,7 +94,12 @@ export async function requestProductsCollection( try { return await apiInstance.invoke('readProduct post /product', criteria); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -93,7 +113,12 @@ export async function requestCategoryProductsCollection( categoryId: categoryId }); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -106,7 +131,12 @@ export async function requestSearchCollectionProducts( ...criteria }); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -124,7 +154,12 @@ export async function requestSeoUrls(routeName: RouteNames, page: number = 1, li ] }); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -145,10 +180,14 @@ export async function requestSeoUrl( } ] }; - console.log(criteria); return await apiInstance.invoke('readSeoUrl post /seo-url', criteria); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -165,7 +204,12 @@ export async function requestCrossSell( } ); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } @@ -173,6 +217,11 @@ export async function requestCart() { try { return apiInstance.invoke('readCart get /checkout/cart?name', {}); } catch (error) { - console.log(error); + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } } From cc073977938858d9ad648bdf03849e2e6debde4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 11:13:05 +0200 Subject: [PATCH 40/57] feat(poc): add decodeURIComponent --- lib/shopware/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 40c5982e9..fb2d1e4a2 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -53,7 +53,7 @@ export async function getMenu(params?: { } export async function getPage(handle: string | []): Promise { - const pageHandle = transformHandle(handle).replace('cms/', ''); + const pageHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', ''); const seoUrlElement = await getFirstSeoUrlElement(pageHandle); if (seoUrlElement) { const category = await getCategory(seoUrlElement); @@ -157,7 +157,7 @@ export async function getCollectionProducts(params?: { }): Promise<{ products: Product[]; total: number; limit: number }> { let products; let category = params?.categoryId; - const collectionName = transformHandle(params?.collection ?? ''); + const collectionName = decodeURIComponent(transformHandle(params?.collection ?? '')); const sorting = getSortingCriteria(params?.sortKey, params?.reverse); if (!category && collectionName !== '') { @@ -203,7 +203,7 @@ export async function getCategory( // This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx export async function getCollection(handle: string | []) { - const collectionName = transformHandle(handle); + const collectionName = decodeURIComponent(transformHandle(handle)); const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { const category = await getCategory(seoUrlElement); @@ -235,7 +235,7 @@ export async function getProductSeoUrls() { export async function getProduct(handle: string | []): Promise { let productSW: ExtendedProduct | undefined; let productId: string | undefined; - const productHandle = transformHandle(handle); + const productHandle = decodeURIComponent(transformHandle(handle)); const seoUrlElement = await getFirstSeoUrlElement(productHandle); if (seoUrlElement) { From 2ee6394d279f51c80e4dd92bc2a316a285385f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 11:42:15 +0200 Subject: [PATCH 41/57] feat(poc): test something --- lib/shopware/api.ts | 3 ++- lib/shopware/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index cfa4f9c3a..377517fe9 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -169,6 +169,7 @@ export async function requestSeoUrl( limit: number = 1 ): Promise { try { + const testing = handle.replace('cms/', '').replace('product/', '').replace('search/', ''); const criteria = { page: page, limit: limit, @@ -176,7 +177,7 @@ export async function requestSeoUrl( { type: 'contains', field: 'seoPathInfo', - value: handle + value: testing } ] }; diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index fb2d1e4a2..7858d9fa5 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -53,7 +53,7 @@ export async function getMenu(params?: { } export async function getPage(handle: string | []): Promise { - const pageHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', ''); + const pageHandle = decodeURIComponent(transformHandle(handle)); const seoUrlElement = await getFirstSeoUrlElement(pageHandle); if (seoUrlElement) { const category = await getCategory(seoUrlElement); From 744243c3d864019ed755046a4160a52f2513725a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 12:00:48 +0200 Subject: [PATCH 42/57] feat(poc): test something --- lib/shopware/api.ts | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 377517fe9..46d924035 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -168,20 +168,53 @@ export async function requestSeoUrl( page: number = 1, limit: number = 1 ): Promise { + console.log(handle); try { - const testing = handle.replace('cms/', '').replace('product/', '').replace('search/', ''); - const criteria = { + const FirstCriteria = { + page: page, + limit: limit, + filter: [ + { + type: 'multi', + // @ts-ignore + operator: 'or', + queries: [ + { + type: 'equals', + field: 'seoPathInfo', + value: handle + '/' + }, + { + type: 'equals', + field: 'seoPathInfo', + value: handle + } + ] + } + ] + }; + // @ts-ignore + const firstResult = await apiInstance.invoke('readSeoUrl post /seo-url', FirstCriteria); + 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: testing + value: lastPart } ] }; - return await apiInstance.invoke('readSeoUrl post /seo-url', criteria); + + return await apiInstance.invoke('readSeoUrl post /seo-url', SecondCriteria); } catch (error) { if (error instanceof ApiClientError) { console.error(error); From 46e003196ff9a7a352294afc090a512c3691d90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 15:56:32 +0200 Subject: [PATCH 43/57] feat(poc): seoUrls yes or no env --- .env.example | 1 + components/carousel.tsx | 6 ++- components/grid/three-items.tsx | 6 ++- lib/shopware/api.ts | 26 +-------- lib/shopware/index.ts | 93 +++++++++++++++++++++------------ lib/shopware/transform.ts | 65 +++++++++++++++-------- 6 files changed, 115 insertions(+), 82 deletions(-) diff --git a/.env.example b/.env.example index a4666dd31..46d641d54 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ SITE_NAME="Next.js Commerce with Shopware Composable Frontends" SHOPWARE_STORE_DOMAIN="" SHOPWARE_API_TYPE="store-api" SHOPWARE_ACCESS_TOKEN="" +SHOPWARE_USE_SEO_URLS="false" diff --git a/components/carousel.tsx b/components/carousel.tsx index 45b845ef5..d30ac01a9 100644 --- a/components/carousel.tsx +++ b/components/carousel.tsx @@ -3,8 +3,12 @@ import Image from 'next/image'; import Link from 'next/link'; export async function Carousel() { + const collectionName = + `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true' + ? 'Summer-BBQ/Hidden-Carousel-Category' + : 'ff7bf3c59f1342a685844fbf8fdf9dc8'; const { products } = await getCollectionProducts({ - collection: 'Summer-BBQ/Hidden-Carousel-Category' + collection: collectionName }); if (!products?.length) return null; diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index a7d2b9010..723bc3095 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -37,8 +37,12 @@ function ThreeItemGridItem({ export async function ThreeItemGrid() { // 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({ - collection: 'Summer-BBQ/Hidden-Category' + collection: collectionName }); if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null; diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 46d924035..193cf5af8 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -168,15 +168,13 @@ export async function requestSeoUrl( page: number = 1, limit: number = 1 ): Promise { - console.log(handle); try { - const FirstCriteria = { + const criteriaSeoUrls = { page: page, limit: limit, filter: [ { type: 'multi', - // @ts-ignore operator: 'or', queries: [ { @@ -194,27 +192,7 @@ export async function requestSeoUrl( ] }; // @ts-ignore - const firstResult = await apiInstance.invoke('readSeoUrl post /seo-url', FirstCriteria); - 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); + return await apiInstance.invoke('readSeoUrl post /seo-url', criteriaSeoUrls); } catch (error) { if (error instanceof ApiClientError) { console.error(error); diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 7858d9fa5..a35478929 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -40,6 +40,7 @@ import { ProductListingCriteria, StoreNavigationTypeSW } from './types'; +const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'; export async function getMenu(params?: { type?: StoreNavigationTypeSW; @@ -53,21 +54,26 @@ export async function getMenu(params?: { } export async function getPage(handle: string | []): Promise { - const pageHandle = decodeURIComponent(transformHandle(handle)); - const seoUrlElement = await getFirstSeoUrlElement(pageHandle); - if (seoUrlElement) { - const category = await getCategory(seoUrlElement); + let seoUrlElement; + let pageIdOrHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', ''); - if (!category) { - console.log('[getPage] Did not found any category with page handle:', pageHandle); + if (useSeoUrls) { + 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) { - console.log('[getPage] Did not found any seoUrl element with page handle:', pageHandle); + const category = await getCategory(pageIdOrHandle); + if (!category) { + console.log('[getPage] Did not found any category with handle:', pageIdOrHandle); } + + return category ? transformPage(category, seoUrlElement) : undefined; } export async function getFirstSeoUrlElement( @@ -91,17 +97,22 @@ export async function getFirstProduct(productId: string): Promise { 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 export async function getCollection(handle: string | []) { - const collectionName = decodeURIComponent(transformHandle(handle)); - const seoUrlElement = await getFirstSeoUrlElement(collectionName); - if (seoUrlElement) { - const category = await getCategory(seoUrlElement); - const path = seoUrlElement.seoPathInfo ?? ''; - if (category) { - const collection = transformCollection(seoUrlElement, category); + let path; + let seoUrlElement; + let categoryIdOrHandle = decodeURIComponent(transformHandle(handle)); - return { - ...collection, - path: `/search/${path}` - }; + if (useSeoUrls) { + seoUrlElement = await getFirstSeoUrlElement(categoryIdOrHandle); + 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() { @@ -236,15 +259,19 @@ export async function getProduct(handle: string | []): Promise 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) return { id: item.id ?? '', title: item.name, children: item.children?.map((item) => transformMenuItem(item, type)) ?? [], - 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 - : '', + path: path, type: item.children && item.children.length > 0 ? 'headline' : 'link' }; } export function transformPage( - seoUrlElement: ApiSchemas['SeoUrl'], - category: ExtendedCategory + category: ExtendedCategory, + seoUrlElement?: ApiSchemas['SeoUrl'] ): Page { let plainHtmlContent; if (category.cmsPage) { @@ -51,20 +57,20 @@ export function transformPage( } return { - id: seoUrlElement.id ?? '', + id: seoUrlElement?.id ?? category.id ?? '', title: category.translated?.metaTitle ?? category.name ?? '', - handle: seoUrlElement.seoPathInfo, + handle: seoUrlElement?.seoPathInfo ?? category.id ?? '', body: plainHtmlContent ?? category.description ?? '', bodySummary: category.translated?.metaDescription ?? category.description ?? '', seo: { title: category.translated?.metaTitle ?? category.name ?? '', description: category.translated?.metaDescription ?? category.description ?? '' }, - createdAt: seoUrlElement.createdAt ?? '', - updatedAt: seoUrlElement.updatedAt ?? '', - routeName: seoUrlElement.routeName, + createdAt: seoUrlElement?.createdAt ?? category.createdAt ?? '', + updatedAt: seoUrlElement?.updatedAt ?? category.updatedAt ?? '', + routeName: seoUrlElement?.routeName, originalCmsPage: category.cmsPage, - foreignKey: seoUrlElement.foreignKey + foreignKey: seoUrlElement?.foreignKey ?? category.id }; } @@ -89,18 +95,22 @@ export function transformToPlainHtmlContent(cmsPage: ExtendedCmsPage): string { } export function transformCollection( - seoUrlElement: ApiSchemas['SeoUrl'], - resCategory: ExtendedCategory + resCategory: ExtendedCategory, + seoUrlElement?: ApiSchemas['SeoUrl'] ) { return { - handle: seoUrlElement.seoPathInfo, + handle: seoUrlElement?.seoPathInfo ?? resCategory.id ?? '', title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', description: resCategory.description ?? '', seo: { title: resCategory.translated?.metaTitle ?? resCategory.name ?? '', 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.type !== 'link') .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) { collection.push({ handle: handle, @@ -183,15 +196,21 @@ export function transformProducts(res: ExtendedProductListingResult): Product[] } export function transformProduct(item: ExtendedProduct): Product { + const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'; const productOptions = transformOptions(item); const productVariants = transformVariants(item); - return { - id: item.id ?? '', - path: + let path = item.id ? item.id : ''; + if (useSeoUrls) { + path = item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo ? item.seoUrls[0].seoPathInfo - : '', + : ''; + } + + return { + id: item.id ?? '', + path: path, availableForSale: item.available ?? false, title: item.translated ? item.translated.name ?? '' : item.name, description: item.translated?.metaDescription From b56e4828e3d4419523d9756cdf37e18b0657cfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 17 Jul 2023 16:32:26 +0200 Subject: [PATCH 44/57] feat(poc): fix parent id --- components/cart/add-to-cart.tsx | 2 +- components/product/variant-selector.tsx | 2 +- lib/shopware/index.ts | 2 +- lib/shopware/transform.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/cart/add-to-cart.tsx b/components/cart/add-to-cart.tsx index eecee848e..3cc403315 100644 --- a/components/cart/add-to-cart.tsx +++ b/components/cart/add-to-cart.tsx @@ -6,7 +6,7 @@ import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useState, useTransition } from 'react'; import LoadingDots from 'components/loading-dots'; -import { ProductVariant } from 'lib/shopify/types'; +import { ProductVariant } from 'lib/shopware/types'; export function AddToCart({ variants, diff --git a/components/product/variant-selector.tsx b/components/product/variant-selector.tsx index 520818516..fd46261f9 100644 --- a/components/product/variant-selector.tsx +++ b/components/product/variant-selector.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { ProductOption, ProductVariant } from 'lib/shopify/types'; +import { ProductOption, ProductVariant } from 'lib/shopware/types'; import { createUrl } from 'lib/utils'; import Link from 'next/link'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index a35478929..d915a3617 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -129,7 +129,7 @@ export async function getSearchCollectionProducts(params?: { const searchCriteria = { ...criteria, ...sorting }; const search = await requestSearchCollectionProducts(searchCriteria); - if (search) { + if (useSeoUrls && search) { search.elements = await changeVariantUrlToParentUrl(search); } diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index 36f45f556..32d1fc248 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -200,7 +200,7 @@ export function transformProduct(item: ExtendedProduct): Product { const productOptions = transformOptions(item); const productVariants = transformVariants(item); - let path = item.id ? item.id : ''; + let path = item.parentId ?? item.id ?? ''; if (useSeoUrls) { path = item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo From a66652fc82e36edbba27294598d7ac135c2f44ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Wed, 19 Jul 2023 09:50:46 +0200 Subject: [PATCH 45/57] feat(poc): add helpers --- components/carousel.tsx | 8 +++---- components/grid/three-items.tsx | 8 +++---- components/layout/navbar/mobile-menu.tsx | 2 +- components/layout/navbar/search.tsx | 1 - lib/shopware/api.ts | 10 ++++----- lib/shopware/helpers.ts | 25 ++++++++++++++++++++++ lib/shopware/index.ts | 18 ++++++++-------- lib/shopware/transform.ts | 27 +++++++++++------------- 8 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 lib/shopware/helpers.ts diff --git a/components/carousel.tsx b/components/carousel.tsx index d30ac01a9..e8a080c5b 100644 --- a/components/carousel.tsx +++ b/components/carousel.tsx @@ -1,12 +1,12 @@ import { getCollectionProducts } from 'lib/shopware'; +import { isSeoUrls } from 'lib/shopware/helpers'; import Image from 'next/image'; import Link from 'next/link'; export async function Carousel() { - const collectionName = - `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true' - ? 'Summer-BBQ/Hidden-Carousel-Category' - : 'ff7bf3c59f1342a685844fbf8fdf9dc8'; + const collectionName = isSeoUrls() + ? 'Summer-BBQ/Hidden-Carousel-Category' + : 'ff7bf3c59f1342a685844fbf8fdf9dc8'; const { products } = await getCollectionProducts({ collection: collectionName }); diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index 723bc3095..27242bef5 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -1,5 +1,6 @@ import { GridTileImage } from 'components/grid/tile'; import { getCollectionProducts } from 'lib/shopware'; +import { isSeoUrls } from 'lib/shopware/helpers'; import type { Product } from 'lib/shopware/types'; import Link from 'next/link'; @@ -37,10 +38,9 @@ function ThreeItemGridItem({ export async function ThreeItemGrid() { // 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 collectionName = isSeoUrls() + ? 'Summer-BBQ/Hidden-Category' + : '4ab73c06d90d4a5cb312209a64480d87'; const { products: homepageItems } = await getCollectionProducts({ collection: collectionName }); diff --git a/components/layout/navbar/mobile-menu.tsx b/components/layout/navbar/mobile-menu.tsx index 6ede894ae..ececc0a96 100644 --- a/components/layout/navbar/mobile-menu.tsx +++ b/components/layout/navbar/mobile-menu.tsx @@ -7,7 +7,7 @@ import { Fragment, useEffect, useState } from 'react'; import CloseIcon from 'components/icons/close'; import MenuIcon from 'components/icons/menu'; -import { Menu } from 'lib/shopify/types'; +import { Menu } from 'lib/shopware/types'; import Search from './search'; export default function MobileMenu({ menu }: { menu: Menu[] }) { diff --git a/components/layout/navbar/search.tsx b/components/layout/navbar/search.tsx index bf4d12230..fff895f43 100644 --- a/components/layout/navbar/search.tsx +++ b/components/layout/navbar/search.tsx @@ -14,7 +14,6 @@ export default function Search() { const val = e.target as HTMLFormElement; const search = val.search as HTMLInputElement; - console.log(`Search:` + search); const newParams = new URLSearchParams(searchParams.toString()); if (search.value) { diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 193cf5af8..5283390a5 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -15,14 +15,12 @@ import { SeoURLResultSW, StoreNavigationTypeSW } from './types'; - -const domainSW = `https://${process.env.SHOPWARE_STORE_DOMAIN!}/${process.env.SHOPWARE_API_TYPE!}`; -const accessTokenSW = `${process.env.SHOPWARE_ACCESS_TOKEN}`; +import { getStoreDomainWithApiType, getAccessToken, getApiType } from 'lib/shopware/helpers'; const apiInstance = createAPIClient({ - baseURL: domainSW, - accessToken: accessTokenSW, - apiType: 'store-api' + baseURL: getStoreDomainWithApiType(), + accessToken: getAccessToken(), + apiType: getApiType() }); // reimport operations return types to use it in application diff --git a/lib/shopware/helpers.ts b/lib/shopware/helpers.ts new file mode 100644 index 000000000..38ea90fb4 --- /dev/null +++ b/lib/shopware/helpers.ts @@ -0,0 +1,25 @@ +export function getAccessToken(): string { + return `${process.env.SHOPWARE_ACCESS_TOKEN}`; +} + +export function getStoreDomainWithApiType(): string { + return getStoreDomain() + '/' + getApiType(); +} + +export function getStoreDomain(protocol: boolean = true): string { + return protocol + ? `https://${process.env.SHOPWARE_STORE_DOMAIN!}` + : `${process.env.SHOPWARE_STORE_DOMAIN!}`; +} + +export function getApiType(): 'store-api' | 'admin-api' { + if (`${process.env.SHOPWARE_API_TYPE!}` === 'admin-api') { + return 'admin-api'; + } + + return 'store-api'; +} + +export function isSeoUrls(): boolean { + return `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'; +} diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index d915a3617..ee82563b7 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -40,7 +40,7 @@ import { ProductListingCriteria, StoreNavigationTypeSW } from './types'; -const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'; +import { isSeoUrls } from 'lib/shopware/helpers'; export async function getMenu(params?: { type?: StoreNavigationTypeSW; @@ -57,7 +57,7 @@ export async function getPage(handle: string | []): Promise { let seoUrlElement; let pageIdOrHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', ''); - if (useSeoUrls) { + if (isSeoUrls()) { seoUrlElement = await getFirstSeoUrlElement(pageIdOrHandle); if (seoUrlElement) { pageIdOrHandle = seoUrlElement.foreignKey; @@ -103,7 +103,7 @@ export async function getSubCollections(collection: string) { const parentCollectionName = Array.isArray(collection) && collection[0] ? collection[0] : undefined; - if (useSeoUrls) { + if (isSeoUrls()) { const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey); @@ -129,7 +129,7 @@ export async function getSearchCollectionProducts(params?: { const searchCriteria = { ...criteria, ...sorting }; const search = await requestSearchCollectionProducts(searchCriteria); - if (useSeoUrls && search) { + if (isSeoUrls() && search) { search.elements = await changeVariantUrlToParentUrl(search); } @@ -171,7 +171,7 @@ export async function getCollectionProducts(params?: { const collectionName = decodeURIComponent(transformHandle(params?.collection ?? '')); const sorting = getSortingCriteria(params?.sortKey, params?.reverse); - if (useSeoUrls && !category && collectionName !== '') { + if (isSeoUrls() && !category && collectionName !== '') { const seoUrlElement = await getFirstSeoUrlElement(collectionName); if (seoUrlElement) { category = seoUrlElement.foreignKey; @@ -184,8 +184,8 @@ export async function getCollectionProducts(params?: { } } - if (!useSeoUrls) { - category = params?.collection ?? undefined; + if (!isSeoUrls()) { + category = collectionName ?? undefined; } if (category) { @@ -222,7 +222,7 @@ export async function getCollection(handle: string | []) { let seoUrlElement; let categoryIdOrHandle = decodeURIComponent(transformHandle(handle)); - if (useSeoUrls) { + if (isSeoUrls()) { seoUrlElement = await getFirstSeoUrlElement(categoryIdOrHandle); if (seoUrlElement) { categoryIdOrHandle = seoUrlElement.foreignKey; @@ -261,7 +261,7 @@ export async function getProduct(handle: string | []): Promise 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 ?? ''; + const path = isSeoUrls() + ? 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) return { @@ -127,9 +127,7 @@ export function transformSubCollection( .filter((item) => item.type !== 'link') .map((item) => { const handle = - item.seoUrls && `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true' - ? findHandle(item.seoUrls, parentCollectionName) - : item.id; + isSeoUrls() && item.seoUrls ? findHandle(item.seoUrls, parentCollectionName) : item.id; if (handle) { collection.push({ handle: handle, @@ -196,12 +194,11 @@ export function transformProducts(res: ExtendedProductListingResult): Product[] } export function transformProduct(item: ExtendedProduct): Product { - const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'; const productOptions = transformOptions(item); const productVariants = transformVariants(item); let path = item.parentId ?? item.id ?? ''; - if (useSeoUrls) { + if (isSeoUrls()) { path = item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo ? item.seoUrls[0].seoPathInfo From 55453755279942782c169668f9050a48178e6317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Fri, 21 Jul 2023 08:43:01 +0200 Subject: [PATCH 46/57] feat(poc): working on addToCart --- components/cart/actions.ts | 4 ++++ components/cart/add-to-cart.tsx | 3 ++- components/cart/modal.tsx | 2 +- lib/shopware/api.ts | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/cart/actions.ts b/components/cart/actions.ts index 75b1a14b7..d21c47697 100644 --- a/components/cart/actions.ts +++ b/components/cart/actions.ts @@ -27,6 +27,7 @@ export const removeItem = async (lineId: string): Promise => return new Error('Missing cartId'); } try { + console.log('removeItem lineId', lineId); //await removeFromCart(cartId, [lineId]); } catch (e) { return new Error('Error removing item', { cause: e }); @@ -48,6 +49,9 @@ export const updateItemQuantity = async ({ return new Error('Missing cartId'); } try { + console.log('lineId', lineId); + console.log('variantId', variantId); + console.log('quantity', quantity); // await updateCart(cartId, [ // { // id: lineId, diff --git a/components/cart/add-to-cart.tsx b/components/cart/add-to-cart.tsx index e353938ee..fbce2622c 100644 --- a/components/cart/add-to-cart.tsx +++ b/components/cart/add-to-cart.tsx @@ -33,7 +33,8 @@ export function AddToCart({ if (variant) { setSelectedVariantId(variant.id); } - }, [searchParams, variants, setSelectedVariantId]); + console.log('selectedVariantId', selectedVariantId); + }, [searchParams, variants, setSelectedVariantId, selectedVariantId]); return (
- {/* */} + {product.descriptionHtml ? ( diff --git a/components/carousel.tsx b/components/carousel.tsx index e8a080c5b..38b403d2e 100644 --- a/components/carousel.tsx +++ b/components/carousel.tsx @@ -20,7 +20,7 @@ export async function Carousel() { {product.featuredImage ? ( { - await requestCart(cartId); + +export const fetchCart = async function (cartId?: string): Promise { + try { + const apiClient = getApiClient(cartId); + const cart = await apiClient.invoke('readCart get /checkout/cart?name', {}); + + return cart; + } catch (error) { + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } + } }; export const addItem = async (variantId: string | undefined): Promise => { - const cartId = cookies().get('sw-context-token')?.value || ''; + const cartId = cookies().get('sw-context-token')?.value; if (!variantId) { - return new Error('Missing cartId or variantId'); + return new Error('Missing variantId'); } + try { - await requestAddToCart(variantId, cartId); - } catch (e) { - console.error('eeeee', e); - return new Error('Error adding item', { cause: e }); + const apiClient = getApiClient(cartId); + apiClient.invoke('addLineItem post /checkout/cart/line-item', { + items: [ + { + id: variantId, + quantity: 1, + referencedId: variantId, + type: 'product' + } + ] + }); + } catch (error) { + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } }; export const removeItem = async (lineId: string): Promise => { - const cartId = cookies().get('cartId')?.value; + const cartId = cookies().get('sw-context-token')?.value; if (!cartId) { return new Error('Missing cartId'); } + try { - console.log('removeItem lineId', lineId); - //await removeFromCart(cartId, [lineId]); - } catch (e) { - return new Error('Error removing item', { cause: e }); + const apiClient = getApiClient(cartId); + await apiClient.invoke('deleteLineItem delete /checkout/cart/line-item?id[]={ids}', { + ids: [lineId] + }); + } catch (error) { + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } }; @@ -43,23 +81,29 @@ export const updateItemQuantity = async ({ variantId: string; quantity: number; }): Promise => { - const cartId = cookies().get('cartId')?.value; + const cartId = cookies().get('sw-context-token')?.value; if (!cartId) { return new Error('Missing cartId'); } + try { - console.log('lineId', lineId); - console.log('variantId', variantId); - console.log('quantity', quantity); - // await updateCart(cartId, [ - // { - // id: lineId, - // merchandiseId: variantId, - // quantity - // } - // ]); - } catch (e) { - return new Error('Error updating item quantity', { cause: e }); + const apiClient = getApiClient(cartId); + await apiClient.invoke('updateLineItem patch /checkout/cart/line-item', { + items: [ + { + id: lineId, + referencedId: variantId, + quantity: quantity + } + ] + }); + } catch (error) { + if (error instanceof ApiClientError) { + console.error(error); + console.error('Details:', error.details); + } else { + console.error('==>', error); + } } }; diff --git a/components/cart/add-to-cart.tsx b/components/cart/add-to-cart.tsx index fbce2622c..0f532d784 100644 --- a/components/cart/add-to-cart.tsx +++ b/components/cart/add-to-cart.tsx @@ -18,7 +18,7 @@ export function AddToCart({ availableForSale: boolean; product: Product; }) { - const [selectedVariantId, setSelectedVariantId] = useState(variants[0]?.id); + const [selectedVariantId, setSelectedVariantId] = useState(product.id); const router = useRouter(); const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); @@ -33,7 +33,6 @@ export function AddToCart({ if (variant) { setSelectedVariantId(variant.id); } - console.log('selectedVariantId', selectedVariantId); }, [searchParams, variants, setSelectedVariantId, selectedVariantId]); return ( @@ -43,7 +42,7 @@ export function AddToCart({ onClick={() => { if (!availableForSale) return; startTransition(async () => { - const error = await addItem(product.id); + const error = await addItem(selectedVariantId); if (error) { console.error(error); diff --git a/components/cart/index.tsx b/components/cart/index.tsx index 88088edd1..290111d15 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -1,20 +1,37 @@ import { fetchCart } from 'components/cart/actions'; -import { getCart } from 'lib/shopware'; import { cookies } from 'next/headers'; import CartModal from './modal'; +import { transformCart } from 'lib/shopware/transform'; export default async function Cart() { + let resCart; const cartId = cookies().get('sw-context-token')?.value; - await fetchCart(cartId); - let cartIdUpdated = false; - const cart = await getCart(cartId); + + if (cartId) { + resCart = await fetchCart(cartId); + } + + let newToken; + if (!cartId && !resCart) { + resCart = await fetchCart(); + if (resCart?.token) { + newToken = resCart?.token; + } + } + + let cart; + if (resCart) { + cart = transformCart(resCart); + } if (!cart) { return null; } - if (cartId !== cart.id) { + let cartIdUpdated = false; + if (cartId !== newToken) { cartIdUpdated = true; } + return ; } diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index 45ed311e0..07294640a 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -122,16 +122,19 @@ export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdU onClick={closeCart} >
- { + {item.merchandise.product.featuredImage.url !== '' && + typeof item.merchandise.product.featuredImage.url !== 'undefined' ? ( + { + ) : null}
diff --git a/lib/shopware/api-extended.ts b/lib/shopware/api-extended.ts index d4915593b..99b67c1d7 100644 --- a/lib/shopware/api-extended.ts +++ b/lib/shopware/api-extended.ts @@ -10,6 +10,8 @@ type operationsWithoutOriginal = Omit< | 'readProductCrossSellings' | 'readProductListing' | 'searchPage' + | 'readCart' + | 'deleteLineItem' >; export type extendedPaths = | 'readCategory post /category/{navigationId}?slots' @@ -19,6 +21,8 @@ export type extendedPaths = | 'readProductCrossSellings post /product/{productId}/cross-selling' | 'readProductListing post /product-listing/{categoryId}' | 'searchPage post /search' + | 'readCart get /checkout/cart?name' + | 'deleteLineItem delete /checkout/cart/line-item?id[]={ids}' | operationPaths; export type extendedOperations = operationsWithoutOriginal & { readCategory: extendedReadCategory; @@ -28,14 +32,56 @@ export type extendedOperations = operationsWithoutOriginal & { readProductCrossSellings: extendedReadProductCrossSellings; readProductListing: extendedReadProductListing; searchPage: extendedSearchPage; + readCart: extendedReadCart; + deleteLineItem: extendedDeleteLineItem; +}; + +export type ExtendedCart = Omit & { + lineItems?: ExtendedLineItem[]; +}; + +export type ExtendedLineItem = schemas['LineItem'] & { + payload: { + updatedAt: string; + createdAt: string; + }; + price: ProductPrice; + cover?: schemas['Media']; +}; + +type ProductPrice = { + unitPrice: number; + quantity: number; + totalPrice: number; + calculatedTaxes: ProductCalculatedTaxes[]; + taxRules: ProductTaxRules[]; + referencePrice?: number; + listPrice?: number; + regulationPrice?: number; + apiAlias: string; +}; + +type ProductCalculatedTaxes = { + tax: number; + taxRate: number; + price: number; + apiAlias: string; +}; + +type ProductTaxRules = { + taxRate: number; + percentage: number; + apiAlias: string; }; export type ExtendedCmsBlock = Omit & { slots?: schemas['CmsSlot'][]; }; + export type ExtendedCmsSection = Omit & { blocks?: ExtendedCmsBlock[]; }; + export type ExtendedCmsPage = Omit & { sections?: ExtendedCmsSection[]; }; @@ -293,3 +339,37 @@ type extendedReadProductListing = { }; }; }; + +type extendedReadCart = { + parameters: { + query?: { + /** The name of the new cart. This parameter will only be used when creating a new cart. */ + name?: string; + }; + }; + responses: { + /** Cart */ + 200: { + content: { + 'application/json': ExtendedCart; + }; + }; + }; +}; + +type extendedDeleteLineItem = { + parameters: { + query: { + /** A list of product identifiers. */ + ids: string[]; + }; + }; + responses: { + /** The updated cart. */ + 200: { + content: { + 'application/json': ExtendedCart; + }; + }; + }; +}; diff --git a/lib/shopware/api.ts b/lib/shopware/api.ts index 662aea3d2..5c14e091d 100644 --- a/lib/shopware/api.ts +++ b/lib/shopware/api.ts @@ -1,6 +1,5 @@ import { createAPIClient, RequestReturnType, ApiClientError } from '@shopware/api-client'; import { operations } from '@shopware/api-client/api-types'; -import { cookies } from 'next/headers'; import { ExtendedCategory, ExtendedCriteria, @@ -18,19 +17,15 @@ import { } from './types'; import { getStoreDomainWithApiType, getAccessToken, getApiType } from 'lib/shopware/helpers'; -function getApiClient(cartId?: string) { - const apiInstance = createAPIClient({ +export function getApiClient(cartId?: string) { + const apiClientParams = { baseURL: getStoreDomainWithApiType(), accessToken: getAccessToken(), apiType: getApiType(), - contextToken: cartId, - onContextChanged(newContextToken: string) { - cookies().set('sw-context-token', newContextToken); - console.log('newContextToken', newContextToken); - } - }); + contextToken: cartId + }; - return apiInstance; + return createAPIClient(apiClientParams); } // reimport operations return types to use it in application @@ -233,9 +228,9 @@ export async function requestCrossSell( } } -export async function requestCart(cartId?: string) { +export async function requestContext(cartId?: string) { try { - return getApiClient(cartId).invoke('readCart get /checkout/cart?name', {}); + return getApiClient(cartId).invoke('readContext get /context', {}); } catch (error) { if (error instanceof ApiClientError) { console.error(error); @@ -245,23 +240,3 @@ export async function requestCart(cartId?: string) { } } } - -export async function requestContext(cartId?: string) { - return getApiClient(cartId).invoke('readCart get /checkout/cart?name', {}); -} - -export async function requestAddToCart(itemId: string, cartId: string) { - try { - return getApiClient(cartId).invoke('addLineItem post /checkout/cart/line-item', { - items: [ - { - referencedId: itemId, - quantity: 1, - type: 'product' - } - ] - }); - } catch (e) { - console.error('e', e); - } -} diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index b380e0050..ef402e133 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -1,5 +1,4 @@ import { - requestCart, requestCategory, requestCategoryList, requestCategoryProductsCollection, @@ -32,7 +31,6 @@ import { } from './transform'; import { ApiSchemas, - Cart, CategoryListingResultSW, Menu, Page, @@ -293,75 +291,3 @@ export async function getProductRecommendations(productId: string): Promise { - const cartData = await requestCart(cartId); - if (cartData) { - // @ToDo: should be moved to transformCart function - const cart: Cart = { - checkoutUrl: 'https://frontends-demo.vercel.app', - cost: { - subtotalAmount: { - amount: cartData.price?.positionPrice?.toString() || '0', - currencyCode: 'EUR' - }, - totalAmount: { - amount: cartData.price?.totalPrice?.toString() || '0', - currencyCode: 'EUR' - }, - totalTaxAmount: { - amount: '0', - currencyCode: 'EUR' - } - }, - id: cartData.token || '', - lines: - cartData.lineItems?.map((lineItem) => ({ - id: lineItem.referencedId || '', - quantity: lineItem.quantity ?? 0, - cost: { - totalAmount: { - amount: (lineItem as any)?.price?.totalPrice || '', - currencyCode: 'EUR' - } - }, - merchandise: { - id: lineItem.referencedId ?? '', - title: lineItem.label ?? '', - selectedOptions: [], - product: { - description: lineItem.description ?? '', - descriptionHtml: lineItem.description ?? '', - id: lineItem.referencedId ?? '', - images: [], - path: '', - seo: { - description: lineItem.description ?? '', - title: lineItem.label ?? '' - }, - availableForSale: true, - featuredImage: (lineItem as any).cover?.url, - handle: '', - options: [], - variants: [], - priceRange: { - minVariantPrice: { - amount: '', // @ToDo: should be correct value - currencyCode: 'EUR' - }, - maxVariantPrice: { - amount: '', // @ToDo: should be correct value - currencyCode: 'EUR' - } - }, - tags: [], - title: lineItem.label ?? '', - updatedAt: (lineItem as any)?.payload?.updatedAt - } - } - })) || [], - totalQuantity: cartData.lineItems?.length || 0 - }; - return cart; - } -} diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts index 1341dee7d..8d30d2a19 100644 --- a/lib/shopware/transform.ts +++ b/lib/shopware/transform.ts @@ -1,5 +1,7 @@ import { ApiSchemas, + Cart, + CartItem, CategoryListingResultSW, Collection, Menu, @@ -9,8 +11,10 @@ import { ProductVariant } from './types'; import { + ExtendedCart, ExtendedCategory, ExtendedCmsPage, + ExtendedLineItem, ExtendedProduct, ExtendedProductListingResult } from './api-extended'; @@ -329,3 +333,78 @@ export function transformHandle(handle: string | []): string { return collectionName ?? ''; } + +export function transformCart(resCart: ExtendedCart): Cart { + return { + checkoutUrl: 'https://frontends-demo.vercel.app', + cost: { + subtotalAmount: { + amount: resCart.price?.positionPrice?.toString() || '0', + currencyCode: 'EUR' + }, + totalAmount: { + amount: resCart.price?.totalPrice?.toString() || '0', + currencyCode: 'EUR' + }, + totalTaxAmount: { + amount: '0', + currencyCode: 'EUR' + } + }, + id: resCart.token ?? '', + lines: + resCart.lineItems?.map((lineItem: ExtendedLineItem) => transformLineItem(lineItem)) || [], + totalQuantity: resCart.lineItems?.length || 0 + }; +} + +function transformLineItem(resLineItem: ExtendedLineItem): CartItem { + return { + id: resLineItem.id || '', + quantity: resLineItem.quantity ?? 0, + cost: { + totalAmount: { + amount: resLineItem.price?.totalPrice.toString() || '', + currencyCode: 'EUR' + } + }, + merchandise: { + id: resLineItem.referencedId ?? '', + title: resLineItem.label ?? '', + selectedOptions: [], + product: { + description: resLineItem.description ?? '', + descriptionHtml: resLineItem.description ?? '', + id: resLineItem.referencedId ?? '', + images: [], + path: resLineItem.referencedId ?? '', + seo: { + description: resLineItem.description ?? '', + title: resLineItem.label ?? '' + }, + availableForSale: true, + featuredImage: { + url: resLineItem.cover?.url ?? '', + altText: resLineItem.cover?.translated?.alt ?? resLineItem.cover?.alt ?? '', + width: Number(resLineItem.cover?.metaData?.width) ?? 0, + height: Number(resLineItem.cover?.metaData?.height) ?? 0 + }, + options: [], + variants: [], + priceRange: { + minVariantPrice: { + amount: '', // @ToDo: should be correct value + currencyCode: 'EUR' + }, + maxVariantPrice: { + amount: '', // @ToDo: should be correct value + currencyCode: 'EUR' + } + }, + tags: [], + title: resLineItem.label ?? '', + updatedAt: resLineItem.payload?.updatedAt ?? resLineItem.payload?.createdAt ?? '' + } + } + }; +} diff --git a/package.json b/package.json index e58c6ff57..ce77e704c 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,11 @@ }, "dependencies": { "@headlessui/react": "^1.7.15", - "@shopware/api-client": "0.0.0-canary-20230713092547", - "@vercel/og": "^0.5.8", + "@shopware/api-client": "0.0.0-canary-20230721083422", + "@vercel/og": "^0.5.9", "clsx": "^1.2.1", "is-empty-iterable": "^3.0.0", - "next": "13.4.10", + "next": "13.4.12", "react": "18.2.0", "react-cookie": "^4.1.1", "react-dom": "18.2.0", @@ -33,18 +33,18 @@ "devDependencies": { "@playwright/test": "^1.36.1", "@tailwindcss/typography": "^0.5.9", - "@types/node": "^20.4.2", + "@types/node": "^20.4.4", "@types/react": "18.2.15", "@types/react-dom": "18.2.7", "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.14", "eslint": "^8.45.0", - "eslint-config-next": "^13.4.10", + "eslint-config-next": "^13.4.12", "eslint-config-prettier": "^8.8.0", "eslint-plugin-unicorn": "^48.0.0", "husky": "^8.0.3", "lint-staged": "^13.2.3", - "postcss": "^8.4.26", + "postcss": "^8.4.27", "prettier": "^3.0.0", "prettier-plugin-tailwindcss": "^0.4.1", "tailwindcss": "^3.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee21b2876..a48b21521 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ dependencies: specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@shopware/api-client': - specifier: 0.0.0-canary-20230713092547 - version: 0.0.0-canary-20230713092547 + specifier: 0.0.0-canary-20230721083422 + version: 0.0.0-canary-20230721083422 '@vercel/og': - specifier: ^0.5.8 - version: 0.5.8 + specifier: ^0.5.9 + version: 0.5.9 clsx: specifier: ^1.2.1 version: 1.2.1 @@ -21,8 +21,8 @@ dependencies: specifier: ^3.0.0 version: 3.0.0 next: - specifier: 13.4.10 - version: 13.4.10(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.12 + version: 13.4.12(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -44,8 +44,8 @@ devDependencies: specifier: ^0.5.9 version: 0.5.9(tailwindcss@3.3.3) '@types/node': - specifier: ^20.4.2 - version: 20.4.2 + specifier: ^20.4.4 + version: 20.4.4 '@types/react': specifier: 18.2.15 version: 18.2.15 @@ -57,13 +57,13 @@ devDependencies: version: 1.0.0 autoprefixer: specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.26) + version: 10.4.14(postcss@8.4.27) eslint: specifier: ^8.45.0 version: 8.45.0 eslint-config-next: - specifier: ^13.4.10 - version: 13.4.10(eslint@8.45.0)(typescript@5.1.6) + specifier: ^13.4.12 + version: 13.4.12(eslint@8.45.0)(typescript@5.1.6) eslint-config-prettier: specifier: ^8.8.0 version: 8.8.0(eslint@8.45.0) @@ -77,14 +77,14 @@ devDependencies: specifier: ^13.2.3 version: 13.2.3 postcss: - specifier: ^8.4.26 - version: 8.4.26 + specifier: ^8.4.27 + version: 8.4.27 prettier: specifier: ^3.0.0 version: 3.0.0 prettier-plugin-tailwindcss: specifier: ^0.4.1 - version: 0.4.1(prettier@3.0.0)https://boxblinkracer.com/blog/shopware-ai-demodata + version: 0.4.1(prettier@3.0.0) tailwindcss: specifier: ^3.3.3 version: 3.3.3 @@ -120,7 +120,7 @@ packages: resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.5https://boxblinkracer.com/blog/shopware-ai-demodata + '@babel/helper-validator-identifier': 7.22.5 chalk: 2.4.2 js-tokens: 4.0.0 dev: true @@ -142,8 +142,8 @@ packages: eslint-visitor-keys: 3.4.1 dev: true - /@eslint-community/regexpp@4.5.1: - resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} + /@eslint-community/regexpp@4.6.0: + resolution: {integrity: sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true @@ -235,18 +235,18 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@next/env@13.4.10: - resolution: {integrity: sha512-3G1yD/XKTSLdihyDSa8JEsaWOELY+OWe08o0LUYzfuHp1zHDA8SObQlzKt+v+wrkkPcnPweoLH1ImZeUa0A1NQ==} + /@next/env@13.4.12: + resolution: {integrity: sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==} dev: false - /@next/eslint-plugin-next@13.4.10: - resolution: {integrity: sha512-YJqyq6vk39JQfvaNtN83t/p5Jy45+bazRL+V4QI8FPd3FBqFYMEsULiwRLgSJMgFqkk4t4JbeZurz+gILEAFpA==} + /@next/eslint-plugin-next@13.4.12: + resolution: {integrity: sha512-6rhK9CdxEgj/j1qvXIyLTWEaeFv7zOK8yJMulz3Owel0uek0U9MJCGzmKgYxM3aAUBo3gKeywCZKyQnJKto60A==} dependencies: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.4.10: - resolution: {integrity: sha512-4bsdfKmmg7mgFGph0UorD1xWfZ5jZEw4kKRHYEeTK9bT1QnMbPVPlVXQRIiFPrhoDQnZUoa6duuPUJIEGLV1Jg==} + /@next/swc-darwin-arm64@13.4.12: + resolution: {integrity: sha512-deUrbCXTMZ6ZhbOoloqecnUeNpUOupi8SE2tx4jPfNS9uyUR9zK4iXBvH65opVcA/9F5I/p8vDXSYbUlbmBjZg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -254,8 +254,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.4.10: - resolution: {integrity: sha512-ngXhUBbcZIWZWqNbQSNxQrB9T1V+wgfCzAor2olYuo/YpaL6mUYNUEgeBMhr8qwV0ARSgKaOp35lRvB7EmCRBg==} + /@next/swc-darwin-x64@13.4.12: + resolution: {integrity: sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -263,8 +263,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.4.10: - resolution: {integrity: sha512-SjCZZCOmHD4uyM75MVArSAmF5Y+IJSGroPRj2v9/jnBT36SYFTORN8Ag/lhw81W9EeexKY/CUg2e9mdebZOwsg==} + /@next/swc-linux-arm64-gnu@13.4.12: + resolution: {integrity: sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -272,8 +272,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.4.10: - resolution: {integrity: sha512-F+VlcWijX5qteoYIOxNiBbNE8ruaWuRlcYyIRK10CugqI/BIeCDzEDyrHIHY8AWwbkTwe6GRHabMdE688Rqq4Q==} + /@next/swc-linux-arm64-musl@13.4.12: + resolution: {integrity: sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -281,8 +281,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.4.10: - resolution: {integrity: sha512-WDv1YtAV07nhfy3i1visr5p/tjiH6CeXp4wX78lzP1jI07t4PnHHG1WEDFOduXh3WT4hG6yN82EQBQHDi7hBrQ==} + /@next/swc-linux-x64-gnu@13.4.12: + resolution: {integrity: sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -290,8 +290,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.4.10: - resolution: {integrity: sha512-zFkzqc737xr6qoBgDa3AwC7jPQzGLjDlkNmt/ljvQJ/Veri5ECdHjZCUuiTUfVjshNIIpki6FuP0RaQYK9iCRg==} + /@next/swc-linux-x64-musl@13.4.12: + resolution: {integrity: sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -299,8 +299,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.4.10: - resolution: {integrity: sha512-IboRS8IWz5mWfnjAdCekkl8s0B7ijpWeDwK2O8CdgZkoCDY0ZQHBSGiJ2KViAG6+BJVfLvcP+a2fh6cdyBr9QQ==} + /@next/swc-win32-arm64-msvc@13.4.12: + resolution: {integrity: sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -308,8 +308,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.4.10: - resolution: {integrity: sha512-bSA+4j8jY4EEiwD/M2bol4uVEu1lBlgsGdvM+mmBm/BbqofNBfaZ2qwSbwE2OwbAmzNdVJRFRXQZ0dkjopTRaQ==} + /@next/swc-win32-ia32-msvc@13.4.12: + resolution: {integrity: sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -317,8 +317,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.4.10: - resolution: {integrity: sha512-g2+tU63yTWmcVQKDGY0MV1PjjqgZtwM4rB1oVVi/v0brdZAcrcTV+04agKzWtvWroyFz6IqtT0MoZJA7PNyLVw==} + /@next/swc-win32-x64-msvc@13.4.12: + resolution: {integrity: sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -352,7 +352,7 @@ packages: engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} dependencies: cross-spawn: 7.0.3 - fast-glob: 3.3.0 + fast-glob: 3.3.1 is-glob: 4.0.3 open: 9.1.0 picocolors: 1.0.0 @@ -364,7 +364,7 @@ packages: engines: {node: '>=16'} hasBin: true dependencies: - '@types/node': 20.4.2 + '@types/node': 20.4.4 playwright-core: 1.36.1 optionalDependencies: fsevents: 2.3.2 @@ -379,8 +379,8 @@ packages: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: true - /@shopware/api-client@0.0.0-canary-20230713092547: - resolution: {integrity: sha512-+5dHwprTnpwtHQl3eletxpvMexqKf9N8fIiecqaeemrsWLkiFyAouP76UzYPVjEF54neOOaAI0DuIBvi2w4WXg==} + /@shopware/api-client@0.0.0-canary-20230721083422: + resolution: {integrity: sha512-5oOFf6SSrxSkksXzFDalp+jwMw5P93KpUNiNzaHyL8XAE/e8B5/DdtXCCMl74sS9fdU3aC5y9mfuyvrPA4t1Zg==} dependencies: ofetch: 1.1.1 dev: false @@ -427,8 +427,8 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.4.2: - resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==} + /@types/node@20.4.4: + resolution: {integrity: sha512-CukZhumInROvLq3+b5gLev+vgpsIqC2D0deQr/yS1WnxvmYLlJXZpaQrQiseMY+6xusl79E04UjWoqyr+t1/Ew==} dev: true /@types/normalize-package-data@2.4.1: @@ -521,8 +521,8 @@ packages: requiresBuild: true dev: true - /@vercel/og@0.5.8: - resolution: {integrity: sha512-WlY5q96e2OmQEUsgqKZrCljNHiD05aErsa7u2z2+pkxyoVSWPHXLYFwHPtp7+IubU4gt4JYfgSbzGQg7n6xeAQ==} + /@vercel/og@0.5.9: + resolution: {integrity: sha512-CtjaV/BVHtNCjRtxGqn8Q6AKFLqcG34Byxr91+mY+4eqyp/09LVe9jEeY9WXjbaKvu8syWPMteTpY+YQUQYzSg==} engines: {node: '>=16'} dependencies: '@resvg/resvg-wasm': 2.4.1 @@ -697,7 +697,7 @@ packages: engines: {node: '>=8'} dev: true - /autoprefixer@10.4.14(postcss@8.4.26): + /autoprefixer@10.4.14(postcss@8.4.27): resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -705,11 +705,11 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.9 - caniuse-lite: 1.0.30001516 + caniuse-lite: 1.0.30001517 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.26 + postcss: 8.4.27 postcss-value-parser: 4.2.0 dev: true @@ -774,8 +774,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001516 - electron-to-chromium: 1.4.461 + caniuse-lite: 1.0.30001517 + electron-to-chromium: 1.4.468 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true @@ -820,8 +820,8 @@ packages: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: false - /caniuse-lite@1.0.30001516: - resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==} + /caniuse-lite@1.0.30001517: + resolution: {integrity: sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==} /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1058,6 +1058,10 @@ packages: engines: {node: '>=6'} dev: true + /destr@2.0.0: + resolution: {integrity: sha512-FJ9RDpf3GicEBvzI3jxc2XhHzbqD8p4ANw/1kPsFBfTvP1b7Gn/Lg1vO7R9J4IVgoMbyUmFrFGZafJ1hPZpvlg==} + dev: false + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -1091,8 +1095,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.461: - resolution: {integrity: sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==} + /electron-to-chromium@1.4.468: + resolution: {integrity: sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag==} dev: true /emoji-regex@10.2.1: @@ -1147,7 +1151,7 @@ packages: is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 is-string: 1.0.7 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 is-weakref: 1.0.2 object-inspect: 1.12.3 object-keys: 1.1.1 @@ -1163,7 +1167,7 @@ packages: typed-array-byte-offset: 1.0.0 typed-array-length: 1.0.4 unbox-primitive: 1.0.2 - which-typed-array: 1.1.10 + which-typed-array: 1.1.11 dev: true /es-set-tostringtag@2.0.1: @@ -1209,8 +1213,8 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-next@13.4.10(eslint@8.45.0)(typescript@5.1.6): - resolution: {integrity: sha512-+JjcM6lQmFR5Mw0ORm9o1CR29+z/uajgSfYAPEGIBxOhTHBgCMs7ysuwi72o7LkMmA8E3N7/h09pSGZxs0s85g==} + /eslint-config-next@13.4.12(eslint@8.45.0)(typescript@5.1.6): + resolution: {integrity: sha512-ZF0r5vxKaVazyZH/37Au/XItiG7qUOBw+HaH3PeyXltIMwXorsn6bdrl0Nn9N5v5v9spc+6GM2ryjugbjF6X2g==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1218,7 +1222,7 @@ packages: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 13.4.10 + '@next/eslint-plugin-next': 13.4.12 '@rushstack/eslint-patch': 1.3.2 '@typescript-eslint/parser': 5.62.0(eslint@8.45.0)(typescript@5.1.6) eslint: 8.45.0 @@ -1226,7 +1230,7 @@ packages: eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.45.0) - eslint-plugin-react: 7.32.2(eslint@8.45.0) + eslint-plugin-react: 7.33.0(eslint@8.45.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.45.0) typescript: 5.1.6 transitivePeerDependencies: @@ -1374,8 +1378,8 @@ packages: eslint: 8.45.0 dev: true - /eslint-plugin-react@7.32.2(eslint@8.45.0): - resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} + /eslint-plugin-react@7.33.0(eslint@8.45.0): + resolution: {integrity: sha512-qewL/8P34WkY8jAqdQxsiL82pDUeT7nhs8IsuXgfgnsEloKCT4miAV9N9kGtx7/KM9NH/NCGUE7Edt9iGxLXFw==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 @@ -1441,7 +1445,7 @@ packages: hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.45.0) - '@eslint-community/regexpp': 4.5.1 + '@eslint-community/regexpp': 4.6.0 '@eslint/eslintrc': 2.1.0 '@eslint/js': 8.44.0 '@humanwhocodes/config-array': 0.11.10 @@ -1548,8 +1552,8 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true - /fast-glob@3.3.0: - resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1758,7 +1762,7 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.0 + fast-glob: 3.3.1 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 @@ -1769,7 +1773,7 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: dir-glob: 3.0.1 - fast-glob: 3.3.0 + fast-glob: 3.3.1 ignore: 5.2.4 merge2: 1.4.1 slash: 4.0.0 @@ -1911,7 +1915,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 dev: true /is-arrayish@0.2.1: @@ -2071,15 +2075,11 @@ packages: has-symbols: 1.0.3 dev: true - /is-typed-array@1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 + which-typed-array: 1.1.11 dev: true /is-weakref@1.0.2: @@ -2354,8 +2354,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next@13.4.10(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-4ep6aKxVTQ7rkUW2fBLhpBr/5oceCuf4KmlUpvG/aXuDTIf9mexNSpabUD6RWPspu6wiJJvozZREhXhueYO36A==} + /next@13.4.12(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-eHfnru9x6NRmTMcjQp6Nz0J4XH9OubmzOa7CkWL+AUrUxpibub3vWwttjduu9No16dug1kq04hiUUpo7J3m3Xw==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: @@ -2372,10 +2372,10 @@ packages: sass: optional: true dependencies: - '@next/env': 13.4.10 + '@next/env': 13.4.12 '@swc/helpers': 0.5.1 busboy: 1.6.0 - caniuse-lite: 1.0.30001516 + caniuse-lite: 1.0.30001517 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2383,15 +2383,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.10 - '@next/swc-darwin-x64': 13.4.10 - '@next/swc-linux-arm64-gnu': 13.4.10 - '@next/swc-linux-arm64-musl': 13.4.10 - '@next/swc-linux-x64-gnu': 13.4.10 - '@next/swc-linux-x64-musl': 13.4.10 - '@next/swc-win32-arm64-msvc': 13.4.10 - '@next/swc-win32-ia32-msvc': 13.4.10 - '@next/swc-win32-x64-msvc': 13.4.10 + '@next/swc-darwin-arm64': 13.4.12 + '@next/swc-darwin-x64': 13.4.12 + '@next/swc-linux-arm64-gnu': 13.4.12 + '@next/swc-linux-arm64-musl': 13.4.12 + '@next/swc-linux-x64-gnu': 13.4.12 + '@next/swc-linux-x64-musl': 13.4.12 + '@next/swc-win32-arm64-msvc': 13.4.12 + '@next/swc-win32-ia32-msvc': 13.4.12 + '@next/swc-win32-x64-msvc': 13.4.12 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -2500,6 +2500,14 @@ packages: es-abstract: 1.22.1 dev: true + /ofetch@1.1.1: + resolution: {integrity: sha512-SSMoktrp9SNLi20BWfB/BnnKcL0RDigXThD/mZBeQxkIRv1xrd9183MtLdsqRYLYSqW0eTr5t8w8MqjNhvoOQQ==} + dependencies: + destr: 2.0.0 + node-fetch-native: 1.2.0 + ufo: 1.1.2 + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -2674,29 +2682,29 @@ packages: engines: {node: '>=4'} dev: true - /postcss-import@15.1.0(postcss@8.4.26): + /postcss-import@15.1.0(postcss@8.4.27): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.26 + postcss: 8.4.27 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.2 dev: true - /postcss-js@4.0.1(postcss@8.4.26): + /postcss-js@4.0.1(postcss@8.4.27): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.26 + postcss: 8.4.27 dev: true - /postcss-load-config@4.0.1(postcss@8.4.26): + /postcss-load-config@4.0.1(postcss@8.4.27): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -2709,17 +2717,17 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.26 + postcss: 8.4.27 yaml: 2.3.1 dev: true - /postcss-nested@6.0.1(postcss@8.4.26): + /postcss-nested@6.0.1(postcss@8.4.27): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.26 + postcss: 8.4.27 postcss-selector-parser: 6.0.13 dev: true @@ -2751,8 +2759,8 @@ packages: source-map-js: 1.0.2 dev: false - /postcss@8.4.26: - resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==} + /postcss@8.4.27: + resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -3281,8 +3289,8 @@ packages: react: 18.2.0 dev: false - /sucrase@3.33.0: - resolution: {integrity: sha512-ARGC7vbufOHfpvyGcZZXFaXCMZ9A4fffOGC5ucOW7+WHDGlAe8LJdf3Jts1sWhDeiI1RSWrKy5Hodl+JWGdW2A==} + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} engines: {node: '>=8'} hasBin: true dependencies: @@ -3332,7 +3340,7 @@ packages: chokidar: 3.5.3 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.0 + fast-glob: 3.3.1 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.19.1 @@ -3341,14 +3349,14 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.26 - postcss-import: 15.1.0(postcss@8.4.26) - postcss-js: 4.0.1(postcss@8.4.26) - postcss-load-config: 4.0.1(postcss@8.4.26) - postcss-nested: 6.0.1(postcss@8.4.26) + postcss: 8.4.27 + postcss-import: 15.1.0(postcss@8.4.27) + postcss-js: 4.0.1(postcss@8.4.27) + postcss-load-config: 4.0.1(postcss@8.4.27) + postcss-nested: 6.0.1(postcss@8.4.27) postcss-selector-parser: 6.0.13 resolve: 1.22.2 - sucrase: 3.33.0 + sucrase: 3.34.0 transitivePeerDependencies: - ts-node dev: true @@ -3458,7 +3466,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 dev: true /typed-array-byte-length@1.0.0: @@ -3468,7 +3476,7 @@ packages: call-bind: 1.0.2 for-each: 0.3.3 has-proto: 1.0.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 dev: true /typed-array-byte-offset@1.0.0: @@ -3479,7 +3487,7 @@ packages: call-bind: 1.0.2 for-each: 0.3.3 has-proto: 1.0.1 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 dev: true /typed-array-length@1.0.4: @@ -3487,7 +3495,7 @@ packages: dependencies: call-bind: 1.0.2 for-each: 0.3.3 - is-typed-array: 1.1.10 + is-typed-array: 1.1.12 dev: true /typescript@5.1.6: @@ -3496,6 +3504,10 @@ packages: hasBin: true dev: true + /ufo@1.1.2: + resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} + dev: false + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -3570,8 +3582,8 @@ packages: is-symbol: 1.0.4 dev: true - /which-typed-array@1.1.10: - resolution: {integrity: sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==} + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 @@ -3579,7 +3591,6 @@ packages: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 dev: true /which@2.0.2: From 66343b5e7371f02ccde88d17a98ac8785d7e43fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 24 Jul 2023 11:41:20 +0200 Subject: [PATCH 48/57] feat(poc): improve addToCart, remove warn --- app/product/[...handle]/page.tsx | 6 +++++- components/cart/actions.ts | 15 +++++++++++++-- components/cart/modal.tsx | 1 - 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/product/[...handle]/page.tsx b/app/product/[...handle]/page.tsx index ceb36da7e..4c54e2dfd 100644 --- a/app/product/[...handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -104,7 +104,11 @@ export default async function ProductPage({ params }: { params: { handle: string ) : null} - +
diff --git a/components/cart/actions.ts b/components/cart/actions.ts index 35b9e9653..45e1b6053 100644 --- a/components/cart/actions.ts +++ b/components/cart/actions.ts @@ -2,7 +2,7 @@ import { ApiClientError } from '@shopware/api-client'; import { getApiClient } from 'lib/shopware/api'; -import { ExtendedCart } from 'lib/shopware/api-extended'; +import { ExtendedCart, ExtendedLineItem } from 'lib/shopware/api-extended'; import { cookies } from 'next/headers'; export const fetchCart = async function (cartId?: string): Promise { @@ -29,12 +29,23 @@ export const addItem = async (variantId: string | undefined): Promise item.id === variantId) as + | ExtendedLineItem + | undefined; + if (itemInCart && itemInCart.quantity) { + quantity = itemInCart.quantity + 1; + } + apiClient.invoke('addLineItem post /checkout/cart/line-item', { items: [ { id: variantId, - quantity: 1, + quantity: quantity, referencedId: variantId, type: 'product' } diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index 07294640a..0d8bc29f6 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -39,7 +39,6 @@ export default function CartModal({ cart, cartIdUpdated }: { cart: Cart; cartIdU }, [setCookie, cartIdUpdated, cart.id]); useEffect(() => { - console.warn('cart modal', cart); // Open cart modal when when quantity changes. if (cart.totalQuantity !== quantityRef.current) { // But only if it's not already open (quantity also changes when editing items in cart). From 2df48267a98b557323ba79c9be391196c0cefd07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Mon, 24 Jul 2023 12:19:27 +0200 Subject: [PATCH 49/57] feat(poc): improve totalQuantity --- components/cart/actions.ts | 2 +- lib/shopware/transform.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/cart/actions.ts b/components/cart/actions.ts index 45e1b6053..d7ebe366b 100644 --- a/components/cart/actions.ts +++ b/components/cart/actions.ts @@ -41,7 +41,7 @@ export const addItem = async (variantId: string | undefined): Promise transformLineItem(lineItem)) || [], - totalQuantity: resCart.lineItems?.length || 0 + totalQuantity: resCart.lineItems ? calculateTotalCartQuantity(resCart.lineItems) : 0 }; } +function calculateTotalCartQuantity(lineItems: ExtendedLineItem[]) { + let totalQuantity = 0; + lineItems.forEach((lineItem) => { + totalQuantity += lineItem.quantity ?? 0; + }); + + return totalQuantity; +} + function transformLineItem(resLineItem: ExtendedLineItem): CartItem { return { id: resLineItem.id || '', From 8acf49ba490a1ff7bbc1c6279b16b15e5b48aa25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Wed, 26 Jul 2023 20:02:21 +0200 Subject: [PATCH 50/57] feat(poc): update @shopware/api-client --- package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ce77e704c..5376c0e6a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@headlessui/react": "^1.7.15", - "@shopware/api-client": "0.0.0-canary-20230721083422", + "@shopware/api-client": "0.0.0-canary-20230725064840", "@vercel/og": "^0.5.9", "clsx": "^1.2.1", "is-empty-iterable": "^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a48b21521..5cff2f7e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@shopware/api-client': - specifier: 0.0.0-canary-20230721083422 - version: 0.0.0-canary-20230721083422 + specifier: 0.0.0-canary-20230725064840 + version: 0.0.0-canary-20230725064840 '@vercel/og': specifier: ^0.5.9 version: 0.5.9 @@ -379,8 +379,8 @@ packages: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: true - /@shopware/api-client@0.0.0-canary-20230721083422: - resolution: {integrity: sha512-5oOFf6SSrxSkksXzFDalp+jwMw5P93KpUNiNzaHyL8XAE/e8B5/DdtXCCMl74sS9fdU3aC5y9mfuyvrPA4t1Zg==} + /@shopware/api-client@0.0.0-canary-20230725064840: + resolution: {integrity: sha512-rubH1IaaJ5JsEE+vFM+Vs0bZErzoDy5nxDdCFfO1eB+6zPP8VetK9fuiebpVAidkf9HKEGQZMiO0Oj6LGKdGGw==} dependencies: ofetch: 1.1.1 dev: false From 5ad4654c55b809f4137ffeb9640c2a5f3a71a972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Meyer?= Date: Thu, 27 Jul 2023 17:14:18 +0200 Subject: [PATCH 51/57] feat(poc): changes for new design --- app/product/[...handle]/page.tsx | 6 +- .../(collection)/[...collection]/page.tsx | 19 +++-- app/search/page.tsx | 16 ++-- components/collection/pagination.tsx | 14 ++-- components/grid/tile.tsx | 3 +- components/icons/logo.tsx | 26 +++--- components/layout/footer-menu.tsx | 32 ++++--- components/layout/footer.tsx | 84 +++++-------------- components/layout/navbar/index.tsx | 10 +-- components/logo-square.tsx | 2 + components/opengraph-image.tsx | 2 +- components/product/gallery.tsx | 2 +- components/product/product-description.tsx | 14 ++-- 13 files changed, 106 insertions(+), 124 deletions(-) diff --git a/app/product/[...handle]/page.tsx b/app/product/[...handle]/page.tsx index e29b6b705..dec1d895f 100644 --- a/app/product/[...handle]/page.tsx +++ b/app/product/[...handle]/page.tsx @@ -8,7 +8,7 @@ import { Gallery } from 'components/product/gallery'; import { ProductDescription } from 'components/product/product-description'; import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; import { getProduct, getProductRecommendations } from 'lib/shopware'; -import { Image } from 'lib/shopify/types'; +import { Image } from 'lib/shopware/types'; import Link from 'next/link'; export const runtime = 'edge'; @@ -132,8 +132,8 @@ async function RelatedProducts({ id }: { id: string }) { currencyCode: product.priceRange.maxVariantPrice.currencyCode }} src={product.featuredImage?.url} - width={600} - height={600} + width={500} + height={500} /> ); diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index a86346b1f..43c7272d9 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -58,13 +58,18 @@ export default async function CategoryPage({ - + {total > limit ? ( + + ) : null}
diff --git a/app/search/page.tsx b/app/search/page.tsx index 992806e69..ae3fb916b 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -25,7 +25,7 @@ export default async function SearchPage({ return ( <> {searchValue && products.length === 0 ? ( -
+

{'There are no products that match '} "{searchValue}" @@ -33,20 +33,22 @@ export default async function SearchPage({

) : null} {products.length > 0 ? ( -
-
+
+
{searchValue ? ( -

+

{`Showing ${products.length} ${resultsText} for `} "{searchValue}"

) : null} -

Good place to add other suggest search terms ;)

+

+ Good place to add other suggested search terms ;) +

- + -
+
diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx index bfd8aec31..57f079ca0 100644 --- a/components/collection/pagination.tsx +++ b/components/collection/pagination.tsx @@ -1,6 +1,8 @@ 'use client'; import ReactPaginate from 'react-paginate'; +import { ArrowLeftIcon } from '@heroicons/react/24/outline'; +import { ArrowRightIcon } from '@heroicons/react/24/outline'; import { createUrl } from 'lib/utils'; import { usePathname, useSearchParams, useRouter } from 'next/navigation'; @@ -52,18 +54,18 @@ export default function Pagination({ initialPage={currentPage} pageCount={pageCount} breakLabel="..." - nextLabel=">>" - previousLabel="<<" + nextLabel= + previousLabel= renderOnZeroPageCount={null} containerClassName="inline sm:flex text-base h-10 mx-auto" activeClassName="active" - pageClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.active]:bg-gray-100" + pageClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.active]:bg-gray-100" pageLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight" - previousClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden" + previousClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden" previousLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight" - nextClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden" + nextClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white [&.disabled]:hidden" nextLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight" - breakClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white" + breakClassName="m-2 sm:m-0 sm:mx-2 text-gray-500 bg-white border rounded-lg border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white" breakLinkClassName="flex items-center justify-center px-4 h-10 ml-0 leading-tight" /> diff --git a/components/grid/tile.tsx b/components/grid/tile.tsx index 783a919b1..3bd9da080 100644 --- a/components/grid/tile.tsx +++ b/components/grid/tile.tsx @@ -33,7 +33,8 @@ export function GridTileImage({ diff --git a/components/icons/logo.tsx b/components/icons/logo.tsx index 46fa02464..4edce3245 100644 --- a/components/icons/logo.tsx +++ b/components/icons/logo.tsx @@ -1,16 +1,18 @@ -import clsx from 'clsx'; +import Image from 'next/image'; -export default function LogoIcon(props: React.ComponentProps<'svg'>) { +export type ImageProps = { + width?: number | string; + height?: number | string; + className?: string; +}; + +export default function LogoIcon(props: ImageProps) { return ( - - - - + Shopware Composable Frontends Logo ); } diff --git a/components/layout/footer-menu.tsx b/components/layout/footer-menu.tsx index 696f61fc0..22be798be 100644 --- a/components/layout/footer-menu.tsx +++ b/components/layout/footer-menu.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { Menu } from 'lib/shopify/types'; +import { Menu } from 'lib/shopware/types'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -15,7 +15,7 @@ const FooterMenuItem = ({ item }: { item: Menu }) => { }, [pathname, item.path]); return ( -
  • +
  • { export default function FooterMenu({ menu }: { menu: Menu[] }) { if (!menu.length) return null; - return ( -