diff --git a/packages/api/src/__generated__/schema.ts b/packages/api/src/__generated__/schema.ts index 83f0480b3f..5f336a5436 100644 --- a/packages/api/src/__generated__/schema.ts +++ b/packages/api/src/__generated__/schema.ts @@ -465,6 +465,8 @@ export type Query = { collection: StoreCollection; /** Returns the details of a product based on the specified locator. */ product: StoreProduct; + /** Returns information about selected products. */ + products: Array; /** Returns if there's a redirect for a search. */ redirect?: Maybe; /** Returns the result of a product, facet, or suggestion search. */ @@ -498,6 +500,11 @@ export type QueryProductArgs = { }; +export type QueryProductsArgs = { + productIds: Array; +}; + + export type QueryRedirectArgs = { selectedFacets?: Maybe>; term?: Maybe; @@ -528,6 +535,21 @@ export type QueryShippingArgs = { postalCode: Scalars['String']; }; +export type SkuSpecificationField = { + __typename?: 'SKUSpecificationField'; + id?: Maybe; + name: Scalars['String']; + originalName?: Maybe; +}; + +export type SkuSpecificationValue = { + __typename?: 'SKUSpecificationValue'; + fieldId?: Maybe; + id?: Maybe; + name: Scalars['String']; + originalName?: Maybe; +}; + /** Search result. */ export type SearchMetadata = { __typename?: 'SearchMetadata'; @@ -607,11 +629,17 @@ export type ShippingSla = { shippingEstimateDate?: Maybe; }; +export type SkuSpecification = { + __typename?: 'SkuSpecification'; + field: SkuSpecificationField; + values: Array; +}; + export type SkuVariants = { __typename?: 'SkuVariants'; /** SKU property values for the current SKU. */ activeVariations?: Maybe; - /** All possible variant combinations of the current product. It also includes the data for each variant. */ + /** All available options for each SKU variant property, indexed by their name. */ allVariantProducts?: Maybe>; /** All available options for each SKU variant property, indexed by their name. */ allVariantsByName?: Maybe; @@ -644,6 +672,20 @@ export type SkuVariantsSlugsMapArgs = { dominantVariantName?: Maybe; }; +export type Specification = { + __typename?: 'Specification'; + name: Scalars['String']; + originalName: Scalars['String']; + values: Array; +}; + +export type SpecificationGroup = { + __typename?: 'SpecificationGroup'; + name: Scalars['String']; + originalName: Scalars['String']; + specifications: Array; +}; + /** Aggregate offer information, for a given SKU that is available to be fulfilled by multiple sellers. */ export type StoreAggregateOffer = { __typename?: 'StoreAggregateOffer'; @@ -989,6 +1031,8 @@ export type StoreProduct = { description: Scalars['String']; /** Global Trade Item Number. */ gtin: Scalars['String']; + /** Indicates whether the product has specifications. */ + hasSpecifications?: Maybe; /** Array of images. */ image: Array; /** Indicates product group related to this product. */ @@ -1007,8 +1051,12 @@ export type StoreProduct = { seo: StoreSeo; /** Stock Keeping Unit. Merchant-specific ID for the product. */ sku: Scalars['String']; + /** Indicate the specifications of a product. */ + skuSpecifications: Array; /** Corresponding collection URL slug, with which to retrieve this entity. */ slug: Scalars['String']; + /** Indicate the specifications of a group of SKUs. */ + specificationGroups: Array; /** Sku Unit Multiplier */ unitMultiplier?: Maybe; }; diff --git a/packages/api/src/platforms/vtex/resolvers/product.ts b/packages/api/src/platforms/vtex/resolvers/product.ts index 0569c7ae60..dc98cc176b 100644 --- a/packages/api/src/platforms/vtex/resolvers/product.ts +++ b/packages/api/src/platforms/vtex/resolvers/product.ts @@ -155,6 +155,11 @@ export const StoreProduct: Record> & { ...propertyValueAttributes, ] }, + hasSpecifications: ({isVariantOf}) => Boolean( + isVariantOf.skuSpecifications?.length + ), + skuSpecifications: ({isVariantOf: { skuSpecifications }}) => skuSpecifications ?? [], + specificationGroups: ({isVariantOf: { specificationGroups }}) => specificationGroups, releaseDate: ({ isVariantOf: { releaseDate } }) => releaseDate ?? '', advertisement: ({ isVariantOf: { advertisement } }) => advertisement, } diff --git a/packages/api/src/platforms/vtex/resolvers/query.ts b/packages/api/src/platforms/vtex/resolvers/query.ts index b2dbe56b48..069af43a57 100644 --- a/packages/api/src/platforms/vtex/resolvers/query.ts +++ b/packages/api/src/platforms/vtex/resolvers/query.ts @@ -21,6 +21,7 @@ import type { QuerySellersArgs, QueryShippingArgs, QueryRedirectArgs, + QueryProductsArgs, } from '../../../__generated__/schema' import type { CategoryTree } from '../clients/commerce/types/CategoryTree' import type { Context } from '../index' @@ -202,6 +203,33 @@ export const Query = { })), } }, + products: async ( + _: unknown, + { productIds }: QueryProductsArgs, + ctx: Context + ) => { + const { + clients: { search }, + } = ctx + + if(!productIds.length) { + return [] + } + + const query = `id:${productIds.join(';')}` + const products = await search.products({ + page: 0, + count: productIds.length, + query, + }) + + return products.products + .map((product) => product.items.map((sku) => enhanceSku(sku, product))) + .flat() + .filter( + (sku) => productIds.includes(sku.itemId) && sku.sellers.length > 0 + ) + }, allCollections: async ( _: unknown, { first, after: maybeAfter }: QueryAllCollectionsArgs, diff --git a/packages/api/src/typeDefs/product.graphql b/packages/api/src/typeDefs/product.graphql index b1a7dfcb50..fac6727f73 100644 --- a/packages/api/src/typeDefs/product.graphql +++ b/packages/api/src/typeDefs/product.graphql @@ -74,6 +74,48 @@ type StoreProduct { Advertisement information about the product. """ advertisement: Advertisement + """ + Indicates whether the product has specifications. + """ + hasSpecifications: Boolean + """ + Indicate the specifications of a product. + """ + skuSpecifications: [SkuSpecification!]! + """ + Indicate the specifications of a group of SKUs. + """ + specificationGroups: [SpecificationGroup!]! +} + +type SkuSpecification { + field: SKUSpecificationField! + values: [SKUSpecificationValue!]! +} + +type SKUSpecificationValue { + name: String! + id: String + fieldId: String + originalName: String +} + +type SKUSpecificationField { + name: String! + originalName: String + id: String +} + +type SpecificationGroup { + name: String! + originalName: String! + specifications: [Specification!]! +} + +type Specification { + name: String! + originalName: String! + values: [String!]! } """ diff --git a/packages/api/src/typeDefs/query.graphql b/packages/api/src/typeDefs/query.graphql index d32bbfbf40..cffcc4881a 100644 --- a/packages/api/src/typeDefs/query.graphql +++ b/packages/api/src/typeDefs/query.graphql @@ -268,6 +268,15 @@ type Query { ): StoreProductConnection! @cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600) + """ + Returns information about selected products. + """ + products( + productIds: [String!]! + + ): [StoreProduct!]! + @cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600) + """ Returns information about all collections. """ diff --git a/packages/api/src/typeDefs/skuVariants.graphql b/packages/api/src/typeDefs/skuVariants.graphql index a56d69d0e0..83e4a02c0c 100644 --- a/packages/api/src/typeDefs/skuVariants.graphql +++ b/packages/api/src/typeDefs/skuVariants.graphql @@ -26,7 +26,7 @@ type SkuVariants { availableVariations(dominantVariantName: String): FormattedVariants """ - All possible variant combinations of the current product. It also includes the data for each variant. + All available options for each SKU variant property, indexed by their name. """ allVariantProducts: [StoreProduct!] } diff --git a/packages/core/@generated/gql.ts b/packages/core/@generated/gql.ts index 8fcec3cc26..0ce69988b8 100644 --- a/packages/core/@generated/gql.ts +++ b/packages/core/@generated/gql.ts @@ -12,11 +12,11 @@ import * as types from './graphql' * Therefore it is highly recommended to use the babel or swc plugin for production. */ const documents = { - '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n': + '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n advertisement {\n adId\n adResponseId\n }\n }\n': types.ProductSummary_ProductFragmentDoc, '\n fragment Filter_facets on StoreFacet {\n ... on StoreFacetRange {\n key\n label\n\n min {\n selected\n absolute\n }\n\n max {\n selected\n absolute\n }\n\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n\n __typename\n }\n }\n': types.Filter_FacetsFragmentDoc, - '\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n\t\t\tskuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n': + '\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n': types.ProductDetailsFragment_ProductFragmentDoc, '\n fragment ProductSKUMatrixSidebarFragment_product on StoreProduct {\n id: productID\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n\t\t\t\t\tsku\n name\n image {\n url\n alternateName\n }\n offers {\n highPrice\n lowPrice\n lowPriceWithTaxes\n offerCount\n priceCurrency\n offers {\n listPrice\n listPriceWithTaxes\n sellingPrice\n priceCurrency\n price\n priceWithTaxes\n priceValidUntil\n itemCondition\n availability\n quantity\n }\n }\n additionalProperty {\n propertyID\n value\n name\n valueReference\n }\n }\n }\n }\n }\n': types.ProductSkuMatrixSidebarFragment_ProductFragmentDoc, @@ -48,10 +48,16 @@ const documents = { types.ClientAllVariantProductsQueryDocument, '\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n': types.ClientManyProductsQueryDocument, + '\n query ClientManyProductsComparisonQuery(\n $productIds: [String!]!\n \n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n } \n': + types.ClientManyProductsComparisonQueryDocument, + '\n fragment ProductComparisonFragment_product on StoreProduct {\n ...ProductDetailsFragment_product,\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n': + types.ProductComparisonFragment_ProductFragmentDoc, '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n': types.ClientProductGalleryQueryDocument, '\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n': types.ClientProductQueryDocument, + '\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n\n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n }\n': + types.ClientManyProductsSelectedQueryDocument, '\n query ClientSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n ...ProductSummary_product\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n': types.ClientSearchSuggestionsQueryDocument, '\n query ClientTopSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientTopSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n }\n': @@ -66,7 +72,7 @@ const documents = { * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function gql( - source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n' + source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n advertisement {\n adId\n adResponseId\n }\n }\n' ): typeof import('./graphql').ProductSummary_ProductFragmentDoc /** * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. @@ -170,6 +176,18 @@ export function gql( export function gql( source: '\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n' ): typeof import('./graphql').ClientManyProductsQueryDocument +/** + * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function gql( + source: '\n query ClientManyProductsComparisonQuery(\n $productIds: [String!]!\n \n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n } \n' +): typeof import('./graphql').ClientManyProductsComparisonQueryDocument +/** + * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function gql( + source: '\n fragment ProductComparisonFragment_product on StoreProduct {\n ...ProductDetailsFragment_product,\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n' +): typeof import('./graphql').ProductComparisonFragment_ProductFragmentDoc /** * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -182,6 +200,12 @@ export function gql( export function gql( source: '\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n' ): typeof import('./graphql').ClientProductQueryDocument +/** + * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function gql( + source: '\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n\n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n }\n' +): typeof import('./graphql').ClientManyProductsSelectedQueryDocument /** * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/core/@generated/graphql.ts b/packages/core/@generated/graphql.ts index 0f71d6e880..9f6ccd7af7 100644 --- a/packages/core/@generated/graphql.ts +++ b/packages/core/@generated/graphql.ts @@ -467,6 +467,8 @@ export type Query = { collection: StoreCollection /** Returns the details of a product based on the specified locator. */ product: StoreProduct + /** Returns information about selected products. */ + products: Array /** Returns if there's a redirect for a search. */ redirect: Maybe /** Returns the result of a product, facet, or suggestion search. */ @@ -495,6 +497,10 @@ export type QueryProductArgs = { locator: Array } +export type QueryProductsArgs = { + productIds: Array +} + export type QueryRedirectArgs = { selectedFacets: InputMaybe> term: InputMaybe @@ -522,6 +528,19 @@ export type QueryShippingArgs = { postalCode: Scalars['String']['input'] } +export type SkuSpecificationField = { + id: Maybe + name: Scalars['String']['output'] + originalName: Maybe +} + +export type SkuSpecificationValue = { + fieldId: Maybe + id: Maybe + name: Scalars['String']['output'] + originalName: Maybe +} + /** Search result. */ export type SearchMetadata = { /** Indicates how the search engine corrected the misspelled word by using fuzzy logic. */ @@ -596,12 +615,19 @@ export type ShippingSla = { shippingEstimateDate: Maybe } +export type SkuSpecification = { + field: SkuSpecificationField + values: Array +} + export type SkuVariants = { /** SKU property values for the current SKU. */ activeVariations: Maybe /** All possible variant combinations of the current product. It also includes the data for each variant. */ allVariantProducts: Maybe> /** All available options for each SKU variant property, indexed by their name. */ + allVariantProducts: Maybe> + /** All available options for each SKU variant property, indexed by their name. */ allVariantsByName: Maybe /** * Available options for each varying SKU property, taking into account the @@ -630,6 +656,18 @@ export type SkuVariantsSlugsMapArgs = { dominantVariantName: InputMaybe } +export type Specification = { + name: Scalars['String']['output'] + originalName: Scalars['String']['output'] + values: Array +} + +export type SpecificationGroup = { + name: Scalars['String']['output'] + originalName: Scalars['String']['output'] + specifications: Array +} + /** Aggregate offer information, for a given SKU that is available to be fulfilled by multiple sellers. */ export type StoreAggregateOffer = { /** Highest price among all sellers. */ @@ -944,6 +982,8 @@ export type StoreProduct = { description: Scalars['String']['output'] /** Global Trade Item Number. */ gtin: Scalars['String']['output'] + /** Indicates whether the product has specifications. */ + hasSpecifications: Maybe /** Array of images. */ image: Array /** Indicates product group related to this product. */ @@ -962,8 +1002,12 @@ export type StoreProduct = { seo: StoreSeo /** Stock Keeping Unit. Merchant-specific ID for the product. */ sku: Scalars['String']['output'] + /** Indicate the specifications of a product. */ + skuSpecifications: Array /** Corresponding collection URL slug, with which to retrieve this entity. */ slug: Scalars['String']['output'] + /** Indicate the specifications of a group of SKUs. */ + specificationGroups: Array /** Sku Unit Multiplier */ unitMultiplier: Maybe } @@ -1136,6 +1180,7 @@ export type ProductSummary_ProductFragment = { sku: string name: string gtin: string + hasSpecifications: boolean | null id: string brand: { name: string; brandName: string } isVariantOf: { productGroupID: string; name: string } @@ -1199,6 +1244,7 @@ export type ProductDetailsFragment_ProductFragment = { activeVariations: any | null slugsMap: any | null availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null } | null } image: Array<{ url: string; alternateName: string }> @@ -1350,6 +1396,7 @@ export type ServerProductQueryQuery = { activeVariations: any | null slugsMap: any | null availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null } | null } additionalProperty: Array<{ @@ -1541,6 +1588,7 @@ export type ClientManyProductsQueryQuery = { sku: string name: string gtin: string + hasSpecifications: boolean | null id: string brand: { name: string; brandName: string } isVariantOf: { productGroupID: string; name: string } @@ -1570,6 +1618,126 @@ export type ClientManyProductsQueryQuery = { } } +export type ClientManyProductsComparisonQueryQueryVariables = Exact<{ + productIds: Array | Scalars['String']['input'] +}> + +export type ClientManyProductsComparisonQueryQuery = { + products: Array<{ + sku: string + name: string + gtin: string + description: string + unitMultiplier: number | null + id: string + skuSpecifications: Array<{ + field: { id: string | null; name: string; originalName: string | null } + values: Array<{ + name: string + id: string | null + fieldId: string | null + originalName: string | null + }> + }> + specificationGroups: Array<{ + name: string + originalName: string + specifications: Array<{ + name: string + originalName: string + values: Array + }> + }> + isVariantOf: { + name: string + productGroupID: string + skuVariants: { + activeVariations: any | null + slugsMap: any | null + availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null + } | null + } + image: Array<{ url: string; alternateName: string }> + brand: { name: string } + offers: { + lowPrice: number + lowPriceWithTaxes: number + offers: Array<{ + availability: string + price: number + priceWithTaxes: number + listPrice: number + listPriceWithTaxes: number + seller: { identifier: string } + }> + } + additionalProperty: Array<{ + propertyID: string + name: string + value: any + valueReference: any + }> + }> +} + +export type ProductComparisonFragment_ProductFragment = { + sku: string + name: string + gtin: string + description: string + unitMultiplier: number | null + id: string + skuSpecifications: Array<{ + field: { id: string | null; name: string; originalName: string | null } + values: Array<{ + name: string + id: string | null + fieldId: string | null + originalName: string | null + }> + }> + specificationGroups: Array<{ + name: string + originalName: string + specifications: Array<{ + name: string + originalName: string + values: Array + }> + }> + isVariantOf: { + name: string + productGroupID: string + skuVariants: { + activeVariations: any | null + slugsMap: any | null + availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null + } | null + } + image: Array<{ url: string; alternateName: string }> + brand: { name: string } + offers: { + lowPrice: number + lowPriceWithTaxes: number + offers: Array<{ + availability: string + price: number + priceWithTaxes: number + listPrice: number + listPriceWithTaxes: number + seller: { identifier: string } + }> + } + additionalProperty: Array<{ + propertyID: string + name: string + value: any + valueReference: any + }> +} + export type ClientProductGalleryQueryQueryVariables = Exact<{ first: Scalars['Int']['input'] after: Scalars['String']['input'] @@ -1635,6 +1803,7 @@ export type ClientProductQueryQuery = { activeVariations: any | null slugsMap: any | null availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null } | null } image: Array<{ url: string; alternateName: string }> @@ -1660,6 +1829,69 @@ export type ClientProductQueryQuery = { } } +export type ClientManyProductsSelectedQueryQueryVariables = Exact<{ + productIds: Array | Scalars['String']['input'] +}> + +export type ClientManyProductsSelectedQueryQuery = { + products: Array<{ + sku: string + name: string + gtin: string + description: string + unitMultiplier: number | null + id: string + skuSpecifications: Array<{ + field: { id: string | null; name: string; originalName: string | null } + values: Array<{ + name: string + id: string | null + fieldId: string | null + originalName: string | null + }> + }> + specificationGroups: Array<{ + name: string + originalName: string + specifications: Array<{ + name: string + originalName: string + values: Array + }> + }> + isVariantOf: { + name: string + productGroupID: string + skuVariants: { + activeVariations: any | null + slugsMap: any | null + availableVariations: any | null + allVariantProducts: Array<{ name: string; productID: string }> | null + } | null + } + image: Array<{ url: string; alternateName: string }> + brand: { name: string } + offers: { + lowPrice: number + lowPriceWithTaxes: number + offers: Array<{ + availability: string + price: number + priceWithTaxes: number + listPrice: number + listPriceWithTaxes: number + seller: { identifier: string } + }> + } + additionalProperty: Array<{ + propertyID: string + name: string + value: any + valueReference: any + }> + }> +} + export type ClientSearchSuggestionsQueryQueryVariables = Exact<{ term: Scalars['String']['input'] selectedFacets: InputMaybe | IStoreSelectedFacet> @@ -1674,6 +1906,7 @@ export type ClientSearchSuggestionsQueryQuery = { sku: string name: string gtin: string + hasSpecifications: boolean | null id: string brand: { name: string; brandName: string } isVariantOf: { productGroupID: string; name: string } @@ -1832,6 +2065,7 @@ export const ProductSummary_ProductFragmentDoc = new TypedDocumentString( value valueReference } + hasSpecifications advertisement { adId adResponseId @@ -2159,6 +2393,39 @@ export const CartMessageFragmentDoc = new TypedDocumentString( `, { fragmentName: 'CartMessage' } ) as unknown as TypedDocumentString +export const CartProductItemFragmentDoc = new TypedDocumentString( + ` + fragment CartProductItem on StoreProduct { + sku + name + unitMultiplier + image { + url + alternateName + } + brand { + name + } + isVariantOf { + productGroupID + name + skuVariants { + activeVariations + slugsMap + availableVariations + } + } + gtin + additionalProperty { + propertyID + name + value + valueReference + } +} + `, + { fragmentName: 'CartProductItem' } +) as unknown as TypedDocumentString export const CartItemFragmentDoc = new TypedDocumentString( ` fragment CartItem on StoreOffer { @@ -2204,6 +2471,201 @@ export const CartItemFragmentDoc = new TypedDocumentString( }`, { fragmentName: 'CartItem' } ) as unknown as TypedDocumentString +export const ProductDetailsFragment_ProductFragmentDoc = + new TypedDocumentString( + ` + fragment ProductDetailsFragment_product on StoreProduct { + id: productID + sku + name + gtin + description + unitMultiplier + isVariantOf { + name + productGroupID + skuVariants { + activeVariations + slugsMap + availableVariations + allVariantProducts { + name + productID + } + } + } + image { + url + alternateName + } + brand { + name + } + offers { + lowPrice + lowPriceWithTaxes + offers { + availability + price + priceWithTaxes + listPrice + listPriceWithTaxes + seller { + identifier + } + } + } + additionalProperty { + propertyID + name + value + valueReference + } + ...CartProductItem +} + fragment CartProductItem on StoreProduct { + sku + name + unitMultiplier + image { + url + alternateName + } + brand { + name + } + isVariantOf { + productGroupID + name + skuVariants { + activeVariations + slugsMap + availableVariations + } + } + gtin + additionalProperty { + propertyID + name + value + valueReference + } +}`, + { fragmentName: 'ProductDetailsFragment_product' } + ) as unknown as TypedDocumentString< + ProductDetailsFragment_ProductFragment, + unknown + > +export const ProductComparisonFragment_ProductFragmentDoc = + new TypedDocumentString( + ` + fragment ProductComparisonFragment_product on StoreProduct { + ...ProductDetailsFragment_product + skuSpecifications { + field { + id + name + originalName + } + values { + name + id + fieldId + originalName + } + } + specificationGroups { + name + originalName + specifications { + name + originalName + values + } + } +} + fragment ProductDetailsFragment_product on StoreProduct { + id: productID + sku + name + gtin + description + unitMultiplier + isVariantOf { + name + productGroupID + skuVariants { + activeVariations + slugsMap + availableVariations + allVariantProducts { + name + productID + } + } + } + image { + url + alternateName + } + brand { + name + } + offers { + lowPrice + lowPriceWithTaxes + offers { + availability + price + priceWithTaxes + listPrice + listPriceWithTaxes + seller { + identifier + } + } + } + additionalProperty { + propertyID + name + value + valueReference + } + ...CartProductItem +} +fragment CartProductItem on StoreProduct { + sku + name + unitMultiplier + image { + url + alternateName + } + brand { + name + } + isVariantOf { + productGroupID + name + skuVariants { + activeVariations + slugsMap + availableVariations + } + } + gtin + additionalProperty { + propertyID + name + value + valueReference + } +}`, + { fragmentName: 'ProductComparisonFragment_product' } + ) as unknown as TypedDocumentString< + ProductComparisonFragment_ProductFragment, + unknown + > export const SearchEvent_MetadataFragmentDoc = new TypedDocumentString( ` fragment SearchEvent_metadata on SearchMetadata { @@ -2226,7 +2688,7 @@ export const ServerCollectionPageQueryDocument = { export const ServerProductQueryDocument = { __meta__: { operationName: 'ServerProductQuery', - operationHash: '46103bee661405bde706d72126fdbf9b0a0c9e6e', + operationHash: 'e855903879c6504e90269e6e010549bc6de933eb', }, } as unknown as TypedDocumentString< ServerProductQueryQuery, @@ -2268,6 +2730,15 @@ export const ClientManyProductsQueryDocument = { ClientManyProductsQueryQuery, ClientManyProductsQueryQueryVariables > +export const ClientManyProductsComparisonQueryDocument = { + __meta__: { + operationName: 'ClientManyProductsComparisonQuery', + operationHash: '7a7474b129c6315a5ba4dac8df99b59b680e4343', + }, +} as unknown as TypedDocumentString< + ClientManyProductsComparisonQueryQuery, + ClientManyProductsComparisonQueryQueryVariables +> export const ClientProductGalleryQueryDocument = { __meta__: { operationName: 'ClientProductGalleryQuery', @@ -2280,16 +2751,25 @@ export const ClientProductGalleryQueryDocument = { export const ClientProductQueryDocument = { __meta__: { operationName: 'ClientProductQuery', - operationHash: '7d121ef8d4dc99174e64e4429a9b977b8bbebed8', + operationHash: '47aa22eb750cb2c529e5eeafb921bfeadb67db71', }, } as unknown as TypedDocumentString< ClientProductQueryQuery, ClientProductQueryQueryVariables > +export const ClientManyProductsSelectedQueryDocument = { + __meta__: { + operationName: 'ClientManyProductsSelectedQuery', + operationHash: '8c0585c37d77be260a89de462975a746db7fb7e9', + }, +} as unknown as TypedDocumentString< + ClientManyProductsSelectedQueryQuery, + ClientManyProductsSelectedQueryQueryVariables +> export const ClientSearchSuggestionsQueryDocument = { __meta__: { operationName: 'ClientSearchSuggestionsQuery', - operationHash: '47e48eaee91d16a4237eb2c1241bc2ed3e2ad9bb', + operationHash: '62463b17174f63e6fef51a240217a637395daad1', }, } as unknown as TypedDocumentString< ClientSearchSuggestionsQueryQuery, diff --git a/packages/core/src/components/product/ProductCard/ProductCard.tsx b/packages/core/src/components/product/ProductCard/ProductCard.tsx index e1e5f3460b..7c3a2fc65b 100644 --- a/packages/core/src/components/product/ProductCard/ProductCard.tsx +++ b/packages/core/src/components/product/ProductCard/ProductCard.tsx @@ -207,6 +207,8 @@ export const fragment = gql(` valueReference } + hasSpecifications + advertisement { adId adResponseId diff --git a/packages/core/src/components/sections/ProductDetails/ProductDetails.tsx b/packages/core/src/components/sections/ProductDetails/ProductDetails.tsx index 29df9bf941..5fbba9266b 100644 --- a/packages/core/src/components/sections/ProductDetails/ProductDetails.tsx +++ b/packages/core/src/components/sections/ProductDetails/ProductDetails.tsx @@ -324,6 +324,10 @@ export const fragment = gql(` activeVariations slugsMap availableVariations + allVariantProducts { + name + productID + } } } diff --git a/packages/core/src/sdk/product/useProductsSelected.ts b/packages/core/src/sdk/product/useProductsSelected.ts new file mode 100644 index 0000000000..6c9cbc5f2e --- /dev/null +++ b/packages/core/src/sdk/product/useProductsSelected.ts @@ -0,0 +1,48 @@ +import { gql } from '@generated' +import { useQuery } from 'src/sdk/graphql/useQuery' +import { useSession } from 'src/sdk/session' +import { useMemo } from 'react' +import { + ClientManyProductsSelectedQueryQuery, + ClientManyProductsSelectedQueryQueryVariables, + ClientSearchSuggestionsQueryQuery, +} from '@generated/graphql' + +const query = gql(` + query ClientManyProductsSelectedQuery( + $productIds: [String!]! + + ) { + products( + productIds: $productIds + ) { + ...ProductComparisonFragment_product + } + } +`) + +export const useProductsSelected = ( + productIds: string[], + enabled: boolean, + processResponse: (data: ClientManyProductsSelectedQueryQuery) => void +) => { + const { channel, locale } = useSession() + const variables = useMemo(() => { + if (!channel) { + throw new Error( + `useProductsSelected: 'channel' from session is an empty string.` + ) + } + + return { productIds } + }, [channel, locale, productIds]) + + return useQuery< + ClientSearchSuggestionsQueryQuery, + ClientManyProductsSelectedQueryQueryVariables + >(query, variables, { + doNotRun: enabled, + onSuccess: (data: ClientManyProductsSelectedQueryQuery) => + processResponse(data), + }) +}