@laioutr/app-shopware 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +146 -0
- package/dist/module.d.mts +88 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +60 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.d.vue.ts +14 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.vue +57 -0
- package/dist/runtime/app/components/ShopwareEmbedFrame.vue.d.ts +14 -0
- package/dist/runtime/app/composables/useShopwareEmbedBridge.d.ts +24 -0
- package/dist/runtime/app/composables/useShopwareEmbedBridge.js +22 -0
- package/dist/runtime/app/const/bridge.d.ts +63 -0
- package/dist/runtime/app/const/bridge.js +9 -0
- package/dist/runtime/app/image/providers/shopware.d.ts +7 -0
- package/dist/runtime/app/image/providers/shopware.js +44 -0
- package/dist/runtime/app/lib/createBridgeHandler.d.ts +44 -0
- package/dist/runtime/app/lib/createBridgeHandler.js +40 -0
- package/dist/runtime/app/lib/isTrustedOrigin.d.ts +9 -0
- package/dist/runtime/app/lib/isTrustedOrigin.js +8 -0
- package/dist/runtime/app/lib/parseBridgeMessage.d.ts +11 -0
- package/dist/runtime/app/lib/parseBridgeMessage.js +34 -0
- package/dist/runtime/app/plugins/pagetypes.d.ts +7 -0
- package/dist/runtime/app/plugins/pagetypes.js +6 -0
- package/dist/runtime/app/public/app-shopware/image-placeholder.svg +7 -0
- package/dist/runtime/app/public/app-shopware/shopware-logo.svg +10 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.d.vue.ts +0 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.vue +44 -0
- package/dist/runtime/app/sections/SectionShopwareCheckout.vue.d.ts +0 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/server/client/shopwareAdminClientFactory.d.ts +4 -0
- package/dist/runtime/server/client/shopwareAdminClientFactory.js +13 -0
- package/dist/runtime/server/client/shopwareClientFactory.d.ts +3 -0
- package/dist/runtime/server/client/shopwareClientFactory.js +18 -0
- package/dist/runtime/server/composable/useGetProductParentId.d.ts +4 -0
- package/dist/runtime/server/composable/useGetProductParentId.js +41 -0
- package/dist/runtime/server/const/checkout.d.ts +7 -0
- package/dist/runtime/server/const/checkout.js +2 -0
- package/dist/runtime/server/const/cookieKeys.d.ts +1 -0
- package/dist/runtime/server/const/cookieKeys.js +1 -0
- package/dist/runtime/server/const/fallbacks.d.ts +5 -0
- package/dist/runtime/server/const/fallbacks.js +14 -0
- package/dist/runtime/server/const/includes.d.ts +1 -0
- package/dist/runtime/server/const/includes.js +1 -0
- package/dist/runtime/server/const/passthroughTokens.d.ts +14 -0
- package/dist/runtime/server/const/passthroughTokens.js +10 -0
- package/dist/runtime/server/media-libraries/shopware.d.ts +20 -0
- package/dist/runtime/server/media-libraries/shopware.js +90 -0
- package/dist/runtime/server/middleware/defineShopware.d.ts +7 -0
- package/dist/runtime/server/middleware/defineShopware.js +33 -0
- package/dist/runtime/server/orchestr/auth/login.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/auth/login.action.js +17 -0
- package/dist/runtime/server/orchestr/cart/add-item.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/add-item.action.js +46 -0
- package/dist/runtime/server/orchestr/cart/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/base.resolver.js +28 -0
- package/dist/runtime/server/orchestr/cart/cart-items.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/cart-items.link.js +10 -0
- package/dist/runtime/server/orchestr/cart/get-checkout-url.action.d.ts +10 -0
- package/dist/runtime/server/orchestr/cart/get-checkout-url.action.js +6 -0
- package/dist/runtime/server/orchestr/cart/get-current.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/get-current.query.js +9 -0
- package/dist/runtime/server/orchestr/cart/remove-items.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/remove-items.action.js +14 -0
- package/dist/runtime/server/orchestr/cart/update-items.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart/update-items.action.js +16 -0
- package/dist/runtime/server/orchestr/cart-item/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/cart-item/base.resolver.js +33 -0
- package/dist/runtime/server/orchestr/category/all.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/all.query.js +15 -0
- package/dist/runtime/server/orchestr/category/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/base.resolver.js +47 -0
- package/dist/runtime/server/orchestr/category/bySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/bySlug.query.js +12 -0
- package/dist/runtime/server/orchestr/category/child-categories.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/child-categories.link.js +36 -0
- package/dist/runtime/server/orchestr/category/listing-page.page-index.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/listing-page.page-index.js +79 -0
- package/dist/runtime/server/orchestr/category/menu-item.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/category/menu-item.link.js +10 -0
- package/dist/runtime/server/orchestr/menu/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/base.resolver.js +63 -0
- package/dist/runtime/server/orchestr/menu/byAlias.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/byAlias.query.js +29 -0
- package/dist/runtime/server/orchestr/menu/byAlias.template.d.ts +2 -0
- package/dist/runtime/server/orchestr/menu/byAlias.template.js +25 -0
- package/dist/runtime/server/orchestr/newsletter/subscribe.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/newsletter/subscribe.action.js +27 -0
- package/dist/runtime/server/orchestr/product/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/base.resolver.js +119 -0
- package/dist/runtime/server/orchestr/product/breadcrumb.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/breadcrumb.link.js +57 -0
- package/dist/runtime/server/orchestr/product/byCategoryId.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategoryId.query.js +47 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.query.js +59 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.template.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/byCategorySlug.template.js +40 -0
- package/dist/runtime/server/orchestr/product/bySlug.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/bySlug.query.js +19 -0
- package/dist/runtime/server/orchestr/product/detail-page.page-index.d.ts +3 -0
- package/dist/runtime/server/orchestr/product/detail-page.page-index.js +58 -0
- package/dist/runtime/server/orchestr/product/reviews.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/reviews.link.js +27 -0
- package/dist/runtime/server/orchestr/product/search.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/search.query.js +43 -0
- package/dist/runtime/server/orchestr/product/variants.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/product/variants.link.js +18 -0
- package/dist/runtime/server/orchestr/product-variant/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/product-variant/base.resolver.js +129 -0
- package/dist/runtime/server/orchestr/review/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/review/base.resolver.js +38 -0
- package/dist/runtime/server/orchestr/review/create-review.action.d.ts +2 -0
- package/dist/runtime/server/orchestr/review/create-review.action.js +14 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search-entry.link.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search-entry.link.js +14 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search.query.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search/suggested-search.query.js +54 -0
- package/dist/runtime/server/orchestr/suggested-search-entry/base.resolver.d.ts +2 -0
- package/dist/runtime/server/orchestr/suggested-search-entry/base.resolver.js +22 -0
- package/dist/runtime/server/orchestr-helper/isLinkSingle.d.ts +2 -0
- package/dist/runtime/server/orchestr-helper/isLinkSingle.js +1 -0
- package/dist/runtime/server/orchestr-helper/matchAndMap.d.ts +1 -0
- package/dist/runtime/server/orchestr-helper/matchAndMap.js +15 -0
- package/dist/runtime/server/orchestr-helper/requestedFields.d.ts +18 -0
- package/dist/runtime/server/orchestr-helper/requestedFields.js +145 -0
- package/dist/runtime/server/routes/adopt-session.post.d.ts +8 -0
- package/dist/runtime/server/routes/adopt-session.post.js +16 -0
- package/dist/runtime/server/routes/checkout.d.ts +16 -0
- package/dist/runtime/server/routes/checkout.js +31 -0
- package/dist/runtime/server/shopware-helper/adoptSession.d.ts +18 -0
- package/dist/runtime/server/shopware-helper/adoptSession.js +23 -0
- package/dist/runtime/server/shopware-helper/bootstrapContextToken.d.ts +18 -0
- package/dist/runtime/server/shopware-helper/bootstrapContextToken.js +22 -0
- package/dist/runtime/server/shopware-helper/cartErrors.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/cartErrors.js +21 -0
- package/dist/runtime/server/shopware-helper/cartMapper.d.ts +14 -0
- package/dist/runtime/server/shopware-helper/cartMapper.js +83 -0
- package/dist/runtime/server/shopware-helper/categoryFlattener.d.ts +75 -0
- package/dist/runtime/server/shopware-helper/categoryFlattener.js +11 -0
- package/dist/runtime/server/shopware-helper/checkoutUrl.d.ts +12 -0
- package/dist/runtime/server/shopware-helper/checkoutUrl.js +5 -0
- package/dist/runtime/server/shopware-helper/contextTokenCookie.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/contextTokenCookie.js +7 -0
- package/dist/runtime/server/shopware-helper/facetMapper.d.ts +38 -0
- package/dist/runtime/server/shopware-helper/facetMapper.js +79 -0
- package/dist/runtime/server/shopware-helper/fetchAllProductVariants.d.ts +137 -0
- package/dist/runtime/server/shopware-helper/fetchAllProductVariants.js +16 -0
- package/dist/runtime/server/shopware-helper/getCart.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/getCart.js +4 -0
- package/dist/runtime/server/shopware-helper/isShopwareId.d.ts +4 -0
- package/dist/runtime/server/shopware-helper/isShopwareId.js +2 -0
- package/dist/runtime/server/shopware-helper/mappers/slugMapper.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/mappers/slugMapper.js +9 -0
- package/dist/runtime/server/shopware-helper/mediaMapper.d.ts +8 -0
- package/dist/runtime/server/shopware-helper/mediaMapper.js +42 -0
- package/dist/runtime/server/shopware-helper/pageIndexEntries.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/pageIndexEntries.js +22 -0
- package/dist/runtime/server/shopware-helper/persistContextToken.d.ts +13 -0
- package/dist/runtime/server/shopware-helper/persistContextToken.js +12 -0
- package/dist/runtime/server/shopware-helper/productLocate.d.ts +14 -0
- package/dist/runtime/server/shopware-helper/productLocate.js +9 -0
- package/dist/runtime/server/shopware-helper/readCategoryPageMeta.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/readCategoryPageMeta.js +19 -0
- package/dist/runtime/server/shopware-helper/readProductPageMeta.d.ts +10 -0
- package/dist/runtime/server/shopware-helper/readProductPageMeta.js +20 -0
- package/dist/runtime/server/shopware-helper/resolveAdoptSession.d.ts +23 -0
- package/dist/runtime/server/shopware-helper/resolveAdoptSession.js +13 -0
- package/dist/runtime/server/shopware-helper/resolveCheckout.d.ts +39 -0
- package/dist/runtime/server/shopware-helper/resolveCheckout.js +24 -0
- package/dist/runtime/server/shopware-helper/sessionHandoff.d.ts +27 -0
- package/dist/runtime/server/shopware-helper/sessionHandoff.js +29 -0
- package/dist/runtime/server/shopware-helper/sortingMapper.d.ts +3 -0
- package/dist/runtime/server/shopware-helper/sortingMapper.js +7 -0
- package/dist/runtime/server/shopware-helper/storeApiPageFetcher.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/storeApiPageFetcher.js +10 -0
- package/dist/runtime/server/shopware-helper/swTranslated.d.ts +7 -0
- package/dist/runtime/server/shopware-helper/swTranslated.js +4 -0
- package/dist/runtime/server/shopware-helper/system/getCurrentSystemEntities.d.ts +20 -0
- package/dist/runtime/server/shopware-helper/system/getCurrentSystemEntities.js +39 -0
- package/dist/runtime/server/shopware-helper/system/getSystemEntities.d.ts +31 -0
- package/dist/runtime/server/shopware-helper/system/getSystemEntities.js +47 -0
- package/dist/runtime/server/shopware-helper/useSeoResolver.d.ts +11 -0
- package/dist/runtime/server/shopware-helper/useSeoResolver.js +70 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/types/shopware.d.ts +29 -0
- package/dist/runtime/server/types/shopware.js +0 -0
- package/dist/runtime/server/types/storeApiTypes.d.ts +5677 -0
- package/dist/runtime/shared/const/checkout.d.ts +18 -0
- package/dist/runtime/shared/const/checkout.js +2 -0
- package/dist/runtime/shared/pageTypes/checkout.pagetype.d.ts +21 -0
- package/dist/runtime/shared/pageTypes/checkout.pagetype.js +12 -0
- package/dist/types.d.mts +3 -0
- package/package.json +96 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ProductBreadcrumbLink } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { BreadcrumbItemBase } from "@laioutr-core/canonical-types/entity/breadcrumb-item";
|
|
3
|
+
import { productsFragmentToken } from "../../const/passthroughTokens.js";
|
|
4
|
+
import { defineShopwareLink } from "../../middleware/defineShopware.js";
|
|
5
|
+
import { entitySlug } from "../../shopware-helper/mappers/slugMapper.js";
|
|
6
|
+
import { swTranslated } from "../../shopware-helper/swTranslated.js";
|
|
7
|
+
export default defineShopwareLink({
|
|
8
|
+
implements: ProductBreadcrumbLink,
|
|
9
|
+
provides: [BreadcrumbItemBase],
|
|
10
|
+
run: async ({ context, entityIds, passthrough, $entity }) => {
|
|
11
|
+
const loadProducts = async () => {
|
|
12
|
+
const response = await context.storefrontClient.invoke("readProduct post /product", {
|
|
13
|
+
body: {
|
|
14
|
+
ids: entityIds,
|
|
15
|
+
associations: {
|
|
16
|
+
seoUrls: {}
|
|
17
|
+
},
|
|
18
|
+
includes: {
|
|
19
|
+
product: ["id", "name", "translated", "seoCategory", "seoUrls"],
|
|
20
|
+
category: ["id", "name", "translated", "seoUrls"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return response.data.elements ?? [];
|
|
25
|
+
};
|
|
26
|
+
const products = passthrough.get(productsFragmentToken) ?? await loadProducts();
|
|
27
|
+
return {
|
|
28
|
+
links: products.map((rawProduct) => {
|
|
29
|
+
const seoCategory = rawProduct.seoCategory;
|
|
30
|
+
const categoryBreadcrumb = seoCategory?.id ? $entity({
|
|
31
|
+
id: `breadcrumb:${seoCategory.id}`,
|
|
32
|
+
base: () => ({
|
|
33
|
+
name: swTranslated(seoCategory, "name"),
|
|
34
|
+
link: {
|
|
35
|
+
type: "reference",
|
|
36
|
+
reference: { type: "Category", slug: entitySlug(seoCategory), id: seoCategory.id }
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}) : void 0;
|
|
40
|
+
const productBreadcrumb = $entity({
|
|
41
|
+
id: `breadcrumb:${rawProduct.id}`,
|
|
42
|
+
base: () => ({
|
|
43
|
+
name: swTranslated(rawProduct, "name"),
|
|
44
|
+
link: {
|
|
45
|
+
type: "reference",
|
|
46
|
+
reference: { type: "Product", slug: entitySlug(rawProduct), id: rawProduct.id }
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
sourceId: rawProduct.id,
|
|
52
|
+
entities: categoryBreadcrumb ? [categoryBreadcrumb, productBreadcrumb] : [productBreadcrumb]
|
|
53
|
+
};
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ProductsByCategoryIdQuery } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { cacheProductParentIds } from "../../composable/useGetProductParentId.js";
|
|
3
|
+
import { parentIdToDefaultVariantIdToken } from "../../const/passthroughTokens.js";
|
|
4
|
+
import { defineShopwareQuery } from "../../middleware/defineShopware.js";
|
|
5
|
+
import {
|
|
6
|
+
mapSelectedFiltersToShopwareFilters,
|
|
7
|
+
mapShopwareAggregationToAvailableFilters
|
|
8
|
+
} from "../../shopware-helper/facetMapper.js";
|
|
9
|
+
import { mapShopwareSortingToOrchestr } from "../../shopware-helper/sortingMapper.js";
|
|
10
|
+
export default defineShopwareQuery(
|
|
11
|
+
ProductsByCategoryIdQuery,
|
|
12
|
+
async ({ context, input, pagination, filter: selectedFilters, sorting, passthrough }) => {
|
|
13
|
+
const { categoryId } = input;
|
|
14
|
+
const { swFilters, swBuiltInFilters } = selectedFilters ? mapSelectedFiltersToShopwareFilters(selectedFilters, context.swCurrency) : {};
|
|
15
|
+
const response = await context.storefrontClient.invoke("readProductListing post /product-listing/{categoryId}", {
|
|
16
|
+
pathParams: { categoryId },
|
|
17
|
+
body: {
|
|
18
|
+
page: pagination.page,
|
|
19
|
+
limit: pagination.limit,
|
|
20
|
+
filter: [...swFilters ?? []],
|
|
21
|
+
order: sorting,
|
|
22
|
+
includes: {
|
|
23
|
+
product: ["id", "parentId"]
|
|
24
|
+
},
|
|
25
|
+
"total-count-mode": "exact",
|
|
26
|
+
"min-price": swBuiltInFilters?.["min-price"],
|
|
27
|
+
"max-price": swBuiltInFilters?.["max-price"],
|
|
28
|
+
manufacturer: swBuiltInFilters?.manufacturer,
|
|
29
|
+
"shipping-free": swBuiltInFilters?.["shipping-free"]
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const availableFilters = mapShopwareAggregationToAvailableFilters(response.data.aggregations);
|
|
33
|
+
const availableSortings = mapShopwareSortingToOrchestr(response.data.availableSortings);
|
|
34
|
+
const parentIdToDefaultVariantId = Object.fromEntries(
|
|
35
|
+
response.data.elements.map((product) => [product.parentId ?? product.id, product.id])
|
|
36
|
+
);
|
|
37
|
+
passthrough.set(parentIdToDefaultVariantIdToken, parentIdToDefaultVariantId);
|
|
38
|
+
cacheProductParentIds(response.data.elements.map((product) => [product.id, product.parentId ?? product.id]));
|
|
39
|
+
return {
|
|
40
|
+
// Return the parent-id, in case the received product is a variant
|
|
41
|
+
ids: response.data.elements.map((product) => product.parentId ?? product.id),
|
|
42
|
+
total: response.data.total,
|
|
43
|
+
availableSortings,
|
|
44
|
+
availableFilters
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ProductsByCategorySlugQuery } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { cacheProductParentIds } from "../../composable/useGetProductParentId.js";
|
|
3
|
+
import { parentIdToDefaultVariantIdToken, productVariantsToken } from "../../const/passthroughTokens.js";
|
|
4
|
+
import { defineShopwareQuery } from "../../middleware/defineShopware.js";
|
|
5
|
+
import {
|
|
6
|
+
mapSelectedFiltersToShopwareFilters,
|
|
7
|
+
mapShopwareAggregationToAvailableFilters
|
|
8
|
+
} from "../../shopware-helper/facetMapper.js";
|
|
9
|
+
import { fetchAllProducts } from "../../shopware-helper/fetchAllProductVariants.js";
|
|
10
|
+
import { mapShopwareSortingToOrchestr } from "../../shopware-helper/sortingMapper.js";
|
|
11
|
+
import { useSeoResolver } from "../../shopware-helper/useSeoResolver.js";
|
|
12
|
+
export default defineShopwareQuery(
|
|
13
|
+
ProductsByCategorySlugQuery,
|
|
14
|
+
async ({ context, input, pagination, filter: selectedFilters, sorting, passthrough }) => {
|
|
15
|
+
const { categorySlug } = input;
|
|
16
|
+
const seoResolver = useSeoResolver(context.storefrontClient);
|
|
17
|
+
const seoEntry = await seoResolver.resolve("category", categorySlug);
|
|
18
|
+
if (!seoEntry) {
|
|
19
|
+
throw new Error(`No seo url found for category slug: ${categorySlug}`);
|
|
20
|
+
}
|
|
21
|
+
const { swFilters, swBuiltInFilters } = selectedFilters ? mapSelectedFiltersToShopwareFilters(selectedFilters, context.swCurrency) : {};
|
|
22
|
+
const response = await context.storefrontClient.invoke("readProductListing post /product-listing/{categoryId}", {
|
|
23
|
+
pathParams: { categoryId: seoEntry.id },
|
|
24
|
+
body: {
|
|
25
|
+
p: pagination.page,
|
|
26
|
+
limit: pagination.limit,
|
|
27
|
+
filter: [...swFilters ?? []],
|
|
28
|
+
order: sorting,
|
|
29
|
+
includes: {
|
|
30
|
+
product: ["id", "parentId"]
|
|
31
|
+
},
|
|
32
|
+
"total-count-mode": "exact",
|
|
33
|
+
"min-price": swBuiltInFilters?.["min-price"],
|
|
34
|
+
"max-price": swBuiltInFilters?.["max-price"],
|
|
35
|
+
manufacturer: swBuiltInFilters?.manufacturer,
|
|
36
|
+
"shipping-free": swBuiltInFilters?.["shipping-free"]
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const availableFilters = mapShopwareAggregationToAvailableFilters(response.data.aggregations);
|
|
40
|
+
const availableSortings = mapShopwareSortingToOrchestr(response.data.availableSortings);
|
|
41
|
+
const parentIdToDefaultVariantId = Object.fromEntries(
|
|
42
|
+
response.data.elements.map((product) => [product.parentId ?? product.id, product.id])
|
|
43
|
+
);
|
|
44
|
+
passthrough.set(parentIdToDefaultVariantIdToken, parentIdToDefaultVariantId);
|
|
45
|
+
cacheProductParentIds(response.data.elements.map((product) => [product.id, product.parentId ?? product.id]));
|
|
46
|
+
const allVariants = await fetchAllProducts(context.storefrontClient, {
|
|
47
|
+
productIds: Object.keys(parentIdToDefaultVariantId),
|
|
48
|
+
loadVariants: true
|
|
49
|
+
});
|
|
50
|
+
passthrough.set(productVariantsToken, allVariants);
|
|
51
|
+
return {
|
|
52
|
+
// Return the parent-id, in case the received product is a variant
|
|
53
|
+
ids: response.data.elements.map((product) => product.parentId ?? product.id),
|
|
54
|
+
total: response.data.total,
|
|
55
|
+
availableSortings,
|
|
56
|
+
availableFilters
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ProductsByCategorySlugQuery } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { defineShopwareQueryTemplateProvider } from "../../middleware/defineShopware.js";
|
|
3
|
+
import { entitySlug } from "../../shopware-helper/mappers/slugMapper.js";
|
|
4
|
+
import { swTranslated } from "../../shopware-helper/swTranslated.js";
|
|
5
|
+
export default defineShopwareQueryTemplateProvider({
|
|
6
|
+
for: ProductsByCategorySlugQuery,
|
|
7
|
+
run: async ({ input, context }) => {
|
|
8
|
+
const { storefrontClient } = context;
|
|
9
|
+
const response = await storefrontClient.invoke("readCategoryList post /category", {
|
|
10
|
+
body: {
|
|
11
|
+
filter: input.term ? [
|
|
12
|
+
{
|
|
13
|
+
type: "multi",
|
|
14
|
+
operator: "or",
|
|
15
|
+
queries: [
|
|
16
|
+
{ type: "contains", field: "name", value: input.term },
|
|
17
|
+
{ type: "contains", field: "metaTitle", value: input.term }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
] : [],
|
|
21
|
+
associations: { seoUrls: {} },
|
|
22
|
+
includes: {
|
|
23
|
+
category: ["id", "name", "translated", "seoUrls"],
|
|
24
|
+
seo_url: ["seoPathInfo", "isCanonical", "routeName"]
|
|
25
|
+
},
|
|
26
|
+
limit: 50
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const templates = [];
|
|
30
|
+
for (const element of response.data.elements ?? []) {
|
|
31
|
+
templates.push({
|
|
32
|
+
input: {
|
|
33
|
+
categorySlug: entitySlug(element)
|
|
34
|
+
},
|
|
35
|
+
label: swTranslated(element, "name")
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return templates;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ProductBySlugQuery } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { useGetProductParentId } from "../../composable/useGetProductParentId.js";
|
|
3
|
+
import { parentIdToDefaultVariantIdToken } from "../../const/passthroughTokens.js";
|
|
4
|
+
import { defineShopwareQuery } from "../../middleware/defineShopware.js";
|
|
5
|
+
import { useSeoResolver } from "../../shopware-helper/useSeoResolver.js";
|
|
6
|
+
export default defineShopwareQuery(ProductBySlugQuery, async ({ context, input, passthrough }) => {
|
|
7
|
+
const seoResolver = useSeoResolver(context.storefrontClient);
|
|
8
|
+
const seoEntry = await seoResolver.resolve("product", input.slug);
|
|
9
|
+
if (!seoEntry) {
|
|
10
|
+
throw new Error(`No product found for slug: ${input.slug}`);
|
|
11
|
+
}
|
|
12
|
+
const getProductParentId = useGetProductParentId(context.storefrontClient);
|
|
13
|
+
const parentId = await getProductParentId(seoEntry.id);
|
|
14
|
+
const productId = parentId ?? seoEntry.id;
|
|
15
|
+
passthrough.set(parentIdToDefaultVariantIdToken, { [productId]: seoEntry.id });
|
|
16
|
+
return {
|
|
17
|
+
id: productId
|
|
18
|
+
};
|
|
19
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { paginate } from "#imports";
|
|
2
|
+
import { ProductDetailPage } from "@laioutr-core/canonical-types/ecommerce";
|
|
3
|
+
import { useGetProductParentId } from "../../composable/useGetProductParentId.js";
|
|
4
|
+
import { defineShopwarePageIndex } from "../../middleware/defineShopware.js";
|
|
5
|
+
import { toProductPageEntry } from "../../shopware-helper/pageIndexEntries.js";
|
|
6
|
+
import { buildProductLocate } from "../../shopware-helper/productLocate.js";
|
|
7
|
+
import { readProductPageMeta } from "../../shopware-helper/readProductPageMeta.js";
|
|
8
|
+
import { SHOPWARE_MAX_LIMIT, storeApiPageFetcher } from "../../shopware-helper/storeApiPageFetcher.js";
|
|
9
|
+
import { useSeoResolver } from "../../shopware-helper/useSeoResolver.js";
|
|
10
|
+
const pageIndexCriteria = {
|
|
11
|
+
associations: { seoUrls: {}, cover: {} },
|
|
12
|
+
includes: {
|
|
13
|
+
product: ["id", "name", "translated", "updatedAt", "seoUrls", "cover"],
|
|
14
|
+
seo_url: ["seoPathInfo", "isCanonical", "routeName"],
|
|
15
|
+
product_media: ["media"],
|
|
16
|
+
media: ["url"]
|
|
17
|
+
},
|
|
18
|
+
// Variants collapse into their parent — one detail page per product, matching `bySlug`'s parent ids.
|
|
19
|
+
filter: [{ type: "equals", field: "parentId", value: null }]
|
|
20
|
+
};
|
|
21
|
+
export default defineShopwarePageIndex({
|
|
22
|
+
for: ProductDetailPage,
|
|
23
|
+
label: "Shopware Product",
|
|
24
|
+
batchSize: SHOPWARE_MAX_LIMIT,
|
|
25
|
+
cache: { ttl: "1h", search: { ttl: "5m" }, locate: { ttl: "1 day" } },
|
|
26
|
+
/**
|
|
27
|
+
* Point lookup, same slug→id path as `bySlug.query.ts`. The SEO resolver yields only an id, so the
|
|
28
|
+
* page metadata costs a second store-API read — absorbed by `cache.locate`, and non-fatal: the
|
|
29
|
+
* subject still resolves when it fails.
|
|
30
|
+
*
|
|
31
|
+
* `locales` is left unset: the store API scopes SEO reads by the `sw-language-id` header, so
|
|
32
|
+
* cross-locale slugs need one read per language and are not implemented. Reporting only the current
|
|
33
|
+
* locale would claim the product has no page in any other.
|
|
34
|
+
*/
|
|
35
|
+
locate: async ({ context, params }) => {
|
|
36
|
+
const client = context.storefrontClient;
|
|
37
|
+
const seoEntry = await useSeoResolver(client).resolve("product", params.slug);
|
|
38
|
+
if (!seoEntry) return void 0;
|
|
39
|
+
const parentId = await useGetProductParentId(client)(seoEntry.id);
|
|
40
|
+
const productId = parentId ?? seoEntry.id;
|
|
41
|
+
return buildProductLocate(productId, await readProductPageMeta(client, productId));
|
|
42
|
+
},
|
|
43
|
+
count: async ({ context }) => {
|
|
44
|
+
const response = await context.storefrontClient.invoke("readProduct post /product", {
|
|
45
|
+
body: { ...pageIndexCriteria, limit: 1, "total-count-mode": "exact" }
|
|
46
|
+
});
|
|
47
|
+
return response.data.total ?? 0;
|
|
48
|
+
},
|
|
49
|
+
search: ({ context, term, take }) => context.storefrontClient.invoke("readProduct post /product", { body: { ...pageIndexCriteria, term, limit: take } }).then((response) => (response.data.elements ?? []).map(toProductPageEntry)),
|
|
50
|
+
list: ({ context, batchSize, startCursor }) => paginate(
|
|
51
|
+
storeApiPageFetcher(
|
|
52
|
+
({ page, limit }) => context.storefrontClient.invoke("readProduct post /product", { body: { ...pageIndexCriteria, page, limit } }).then((response) => response.data),
|
|
53
|
+
toProductPageEntry,
|
|
54
|
+
batchSize
|
|
55
|
+
),
|
|
56
|
+
startCursor
|
|
57
|
+
)
|
|
58
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ProductReviewsLink } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { currentProductIdsToken } from "../../const/passthroughTokens.js";
|
|
3
|
+
import { defineShopwareLink } from "../../middleware/defineShopware.js";
|
|
4
|
+
export default defineShopwareLink(ProductReviewsLink, async ({ entityIds, context, pagination, passthrough }) => {
|
|
5
|
+
const { storefrontClient } = context;
|
|
6
|
+
const productToReviews = {};
|
|
7
|
+
await Promise.all(
|
|
8
|
+
entityIds.map(async (entityId) => {
|
|
9
|
+
const response = await storefrontClient.invoke("readProductReviews post /product/{productId}/reviews", {
|
|
10
|
+
pathParams: { productId: entityId },
|
|
11
|
+
body: {
|
|
12
|
+
page: pagination.page,
|
|
13
|
+
limit: pagination.limit,
|
|
14
|
+
includes: {
|
|
15
|
+
product_review: ["id"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const reviewIds = (response.data.elements ?? []).map((element) => element.id);
|
|
20
|
+
productToReviews[entityId] = reviewIds;
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
passthrough.set(currentProductIdsToken, entityIds);
|
|
24
|
+
return {
|
|
25
|
+
links: Object.entries(productToReviews).map(([productId, reviewsIds]) => ({ sourceId: productId, targetIds: reviewsIds }))
|
|
26
|
+
};
|
|
27
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ProductSearchQuery } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { cacheProductParentIds } from "../../composable/useGetProductParentId.js";
|
|
3
|
+
import { parentIdToDefaultVariantIdToken } from "../../const/passthroughTokens.js";
|
|
4
|
+
import { defineShopwareQuery } from "../../middleware/defineShopware.js";
|
|
5
|
+
import {
|
|
6
|
+
mapSelectedFiltersToShopwareFilters,
|
|
7
|
+
mapShopwareAggregationToAvailableFilters
|
|
8
|
+
} from "../../shopware-helper/facetMapper.js";
|
|
9
|
+
import { mapShopwareSortingToOrchestr } from "../../shopware-helper/sortingMapper.js";
|
|
10
|
+
export default defineShopwareQuery(ProductSearchQuery, async ({ context, input, pagination, filter: selectedFilters, passthrough }) => {
|
|
11
|
+
const { query } = input;
|
|
12
|
+
const { swFilters, swBuiltInFilters } = selectedFilters ? mapSelectedFiltersToShopwareFilters(selectedFilters, context.swCurrency) : {};
|
|
13
|
+
const response = await context.storefrontClient.invoke("searchSuggest post /search-suggest", {
|
|
14
|
+
// Shopware API client exposes incorrect/incomplete types for this endpoint's body :<
|
|
15
|
+
body: {
|
|
16
|
+
search: query,
|
|
17
|
+
p: pagination.page,
|
|
18
|
+
limit: pagination.limit,
|
|
19
|
+
filter: [...swFilters ?? []],
|
|
20
|
+
includes: {
|
|
21
|
+
product: ["id", "parentId"]
|
|
22
|
+
},
|
|
23
|
+
"min-price": swBuiltInFilters?.["min-price"],
|
|
24
|
+
"max-price": swBuiltInFilters?.["max-price"],
|
|
25
|
+
manufacturer: swBuiltInFilters?.manufacturer,
|
|
26
|
+
"shipping-free": swBuiltInFilters?.["shipping-free"]
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const availableFilters = mapShopwareAggregationToAvailableFilters(response.data.aggregations);
|
|
30
|
+
const availableSortings = mapShopwareSortingToOrchestr(response.data.availableSortings ?? []);
|
|
31
|
+
const parentIdToDefaultVariantId = Object.fromEntries(
|
|
32
|
+
response.data.elements.map((product) => [product.parentId ?? product.id, product.id])
|
|
33
|
+
);
|
|
34
|
+
passthrough.set(parentIdToDefaultVariantIdToken, parentIdToDefaultVariantId);
|
|
35
|
+
cacheProductParentIds(response.data.elements.map((product) => [product.id, product.parentId ?? product.id]));
|
|
36
|
+
return {
|
|
37
|
+
// Return the parent-id, in case the received product is a variant
|
|
38
|
+
ids: response.data.elements.map((product) => product.parentId ?? product.id),
|
|
39
|
+
total: response.data.total,
|
|
40
|
+
availableFilters,
|
|
41
|
+
availableSortings
|
|
42
|
+
};
|
|
43
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProductVariantsLink } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { productVariantsToken } from "../../const/passthroughTokens.js";
|
|
3
|
+
import { defineShopwareLink } from "../../middleware/defineShopware.js";
|
|
4
|
+
import { fetchAllProducts } from "../../shopware-helper/fetchAllProductVariants.js";
|
|
5
|
+
export default defineShopwareLink(ProductVariantsLink, async ({ entityIds, context, passthrough }) => {
|
|
6
|
+
const allVariants = passthrough.get(productVariantsToken) ?? await fetchAllProducts(context.storefrontClient, { productIds: entityIds, loadVariants: true });
|
|
7
|
+
passthrough.set(productVariantsToken, allVariants);
|
|
8
|
+
return {
|
|
9
|
+
links: entityIds.map((productId) => {
|
|
10
|
+
const product = allVariants.find((variant) => variant.id === productId);
|
|
11
|
+
return {
|
|
12
|
+
sourceId: productId,
|
|
13
|
+
// A products children are its variants. If it has no children, it is a single variant product.
|
|
14
|
+
targetIds: product?.children && product?.children.length > 0 ? product?.children.map((child) => child.id) : [productId]
|
|
15
|
+
};
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Money } from "@screeny05/ts-money";
|
|
2
|
+
import {
|
|
3
|
+
ProductVariantAvailability,
|
|
4
|
+
ProductVariantBase,
|
|
5
|
+
ProductVariantInfo,
|
|
6
|
+
ProductVariantOptions,
|
|
7
|
+
ProductVariantPrices,
|
|
8
|
+
ProductVariantQuantityPrices,
|
|
9
|
+
ProductVariantQuantityRule,
|
|
10
|
+
ProductVariantShipping
|
|
11
|
+
} from "@laioutr-core/canonical-types/entity/product-variant";
|
|
12
|
+
import { productVariantsToken } from "../../const/passthroughTokens.js";
|
|
13
|
+
import { defineShopwareComponentResolver } from "../../middleware/defineShopware.js";
|
|
14
|
+
import { resolveProductVariantFields } from "../../orchestr-helper/requestedFields.js";
|
|
15
|
+
import { mapMedia } from "../../shopware-helper/mediaMapper.js";
|
|
16
|
+
import { swTranslated } from "../../shopware-helper/swTranslated.js";
|
|
17
|
+
export default defineShopwareComponentResolver({
|
|
18
|
+
entityType: "ProductVariant",
|
|
19
|
+
label: "Shopware Product Variant Connector",
|
|
20
|
+
provides: [
|
|
21
|
+
ProductVariantBase,
|
|
22
|
+
ProductVariantInfo,
|
|
23
|
+
ProductVariantAvailability,
|
|
24
|
+
ProductVariantPrices,
|
|
25
|
+
ProductVariantQuantityPrices,
|
|
26
|
+
ProductVariantQuantityRule,
|
|
27
|
+
ProductVariantShipping,
|
|
28
|
+
ProductVariantOptions
|
|
29
|
+
],
|
|
30
|
+
resolve: async ({ entityIds, context, clientEnv, $entity, passthrough }) => {
|
|
31
|
+
const { currency } = clientEnv;
|
|
32
|
+
const loadedVariants = passthrough.get(productVariantsToken) ?? [];
|
|
33
|
+
const missingVariantIds = entityIds.filter((id) => !loadedVariants.some((variant) => variant.id === id));
|
|
34
|
+
if (missingVariantIds.length > 0) {
|
|
35
|
+
const response = await context.storefrontClient.invoke("readProduct post /product", {
|
|
36
|
+
body: {
|
|
37
|
+
ids: missingVariantIds,
|
|
38
|
+
...resolveProductVariantFields()
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
loadedVariants.push(...response.data.elements ?? []);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
entities: entityIds.map((entityId) => loadedVariants.find((variant) => variant.id === entityId)).filter((entity) => !!entity).map(
|
|
45
|
+
(entity) => $entity({
|
|
46
|
+
id: entity.id,
|
|
47
|
+
base: () => ({
|
|
48
|
+
// Shopware SKU/product number
|
|
49
|
+
sku: entity.productNumber ?? entity.ean ?? entity.id,
|
|
50
|
+
name: entity.name,
|
|
51
|
+
// Shopware stores GTIN/EAN in `ean`
|
|
52
|
+
gtin: entity.ean ?? void 0
|
|
53
|
+
}),
|
|
54
|
+
availability: () => ({
|
|
55
|
+
status: entity.available ? "inStock" : "outOfStock",
|
|
56
|
+
// Prefer availableStock if you include it; fallback to stock
|
|
57
|
+
quantity: entity.availableStock ?? entity.stock ?? 0
|
|
58
|
+
}),
|
|
59
|
+
prices: () => {
|
|
60
|
+
const unit = entity.calculatedPrice?.unitPrice ?? 0;
|
|
61
|
+
const listPrice = entity.calculatedPrice?.listPrice?.price;
|
|
62
|
+
const price = Money.fromDecimal(Number(unit), currency);
|
|
63
|
+
const compareAtPrice = typeof listPrice === "number" && listPrice > unit ? Money.fromDecimal(Number(listPrice), currency) : void 0;
|
|
64
|
+
const rp = entity.calculatedPrice?.referencePrice;
|
|
65
|
+
return {
|
|
66
|
+
price,
|
|
67
|
+
isOnSale: !!compareAtPrice,
|
|
68
|
+
strikethroughPrice: compareAtPrice,
|
|
69
|
+
savingsPercent: compareAtPrice ? 100 - price.percentageOf(compareAtPrice) : void 0,
|
|
70
|
+
unitPrice: rp ? {
|
|
71
|
+
quantity: {
|
|
72
|
+
value: rp.purchaseUnit ?? 1,
|
|
73
|
+
// if you have your own unit normalizer, call it here
|
|
74
|
+
unit: rp.unitName ?? rp.referenceUnit ?? void 0
|
|
75
|
+
},
|
|
76
|
+
price: Money.fromDecimal(Number(rp.price), currency),
|
|
77
|
+
reference: {
|
|
78
|
+
value: rp.referenceUnit ?? 1,
|
|
79
|
+
unit: rp.unitName ?? rp.referenceUnit ?? void 0
|
|
80
|
+
}
|
|
81
|
+
} : void 0
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
info: () => {
|
|
85
|
+
const allMedia = (entity.media ?? []).map((m) => m.media);
|
|
86
|
+
const media = entity.cover?.media ? [entity.cover.media, ...allMedia] : allMedia;
|
|
87
|
+
return {
|
|
88
|
+
image: media.map(mapMedia).find((m) => m.type === "image")
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
// Tiered/quantity prices:
|
|
92
|
+
// Shopware exposes calculatedPrices[] for price tiers (quantityStart/End etc.)
|
|
93
|
+
quantityPrices: () => {
|
|
94
|
+
const baseUnit = entity.calculatedPrice?.unitPrice ?? 0;
|
|
95
|
+
const base = Money.fromDecimal(Number(baseUnit), currency);
|
|
96
|
+
const tiers = Array.isArray(entity.calculatedPrices) ? entity.calculatedPrices : [];
|
|
97
|
+
return tiers.map((cp) => {
|
|
98
|
+
const tierUnit = cp?.unitPrice ?? baseUnit;
|
|
99
|
+
const price = Money.fromDecimal(Number(tierUnit), currency);
|
|
100
|
+
const qty = cp?.quantity ?? 1;
|
|
101
|
+
return {
|
|
102
|
+
quantity: qty,
|
|
103
|
+
price,
|
|
104
|
+
savingsPercent: price.percentageOf(base)
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
quantityRule: () => ({
|
|
109
|
+
min: entity.minPurchase ?? 1,
|
|
110
|
+
increment: entity.purchaseSteps ?? 1,
|
|
111
|
+
max: entity.maxPurchase ?? void 0
|
|
112
|
+
}),
|
|
113
|
+
shipping: () => ({
|
|
114
|
+
// Shopware doesn’t expose a direct "requiresShipping" boolean.
|
|
115
|
+
// If you track virtual/downloadable yourself, plug it here.
|
|
116
|
+
// Otherwise assume physical → requires shipping.
|
|
117
|
+
required: true
|
|
118
|
+
}),
|
|
119
|
+
options: () => ({
|
|
120
|
+
selected: entity.options?.map((option) => ({
|
|
121
|
+
name: swTranslated(option.group, "name") ?? swTranslated(option, "name") ?? "Unknown Property",
|
|
122
|
+
value: swTranslated(option, "name") ?? swTranslated(option, "option") ?? "Unknown Value"
|
|
123
|
+
})) ?? []
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ReviewBase } from "@laioutr-core/canonical-types/entity/review";
|
|
2
|
+
import { currentProductIdsToken } from "../../const/passthroughTokens.js";
|
|
3
|
+
import { defineShopwareComponentResolver } from "../../middleware/defineShopware.js";
|
|
4
|
+
export default defineShopwareComponentResolver({
|
|
5
|
+
label: "Shopware Product Review Connector",
|
|
6
|
+
entityType: "Review",
|
|
7
|
+
provides: [ReviewBase],
|
|
8
|
+
resolve: async ({ entityIds, $entity, context, passthrough }) => {
|
|
9
|
+
const currentProductIds = passthrough.get(currentProductIdsToken);
|
|
10
|
+
if (!currentProductIds) throw new Error("Missing `currentProductIdsToken` in passthrough.");
|
|
11
|
+
let entities = [];
|
|
12
|
+
for (const productId of currentProductIds) {
|
|
13
|
+
const res = await context.storefrontClient.invoke("readProductReviews post /product/{productId}/reviews", {
|
|
14
|
+
pathParams: { productId },
|
|
15
|
+
body: {
|
|
16
|
+
ids: entityIds,
|
|
17
|
+
includes: {
|
|
18
|
+
product_review: ["id", "title", "content", "points", "externalUser"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
entities = entities.concat(...res.data.elements ?? []);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
entities: entities.map(
|
|
26
|
+
(review) => $entity({
|
|
27
|
+
id: review.id,
|
|
28
|
+
base: () => ({
|
|
29
|
+
title: review.title,
|
|
30
|
+
shortContent: review.content,
|
|
31
|
+
createdAt: new Date(review.createdAt),
|
|
32
|
+
rating: review.points
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CreateReviewAction } from "@laioutr-core/canonical-types/ecommerce";
|
|
2
|
+
import { defineShopwareAction } from "../../middleware/defineShopware.js";
|
|
3
|
+
export default defineShopwareAction(CreateReviewAction, async ({ context, input }) => {
|
|
4
|
+
try {
|
|
5
|
+
const { productId, name, email, title, content, points } = input;
|
|
6
|
+
const res = await context.storefrontClient.invoke("saveProductReview post /product/{productId}/review", {
|
|
7
|
+
pathParams: { productId },
|
|
8
|
+
body: { name, email, title, content, points }
|
|
9
|
+
});
|
|
10
|
+
return { success: res.status === 200 };
|
|
11
|
+
} catch (err) {
|
|
12
|
+
return { success: false, message: err.message };
|
|
13
|
+
}
|
|
14
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SuggestedSearchEntriesLink } from "@laioutr-core/canonical-types/suggested-search";
|
|
2
|
+
import { suggestionResultsFragmentToken } from "../../const/passthroughTokens.js";
|
|
3
|
+
import { defineShopwareLink } from "../../middleware/defineShopware.js";
|
|
4
|
+
export default defineShopwareLink(SuggestedSearchEntriesLink, async ({ passthrough }) => {
|
|
5
|
+
const results = passthrough.require(suggestionResultsFragmentToken);
|
|
6
|
+
return {
|
|
7
|
+
links: [
|
|
8
|
+
{
|
|
9
|
+
sourceId: results.id,
|
|
10
|
+
targetIds: results.suggestions.map((res) => res.id)
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
});
|