@faststore/api 1.10.30 → 1.11.3
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/CHANGELOG.md +31 -0
- package/dist/__generated__/schema.d.ts +94 -0
- package/dist/api.cjs.development.js +249 -25
- package/dist/api.cjs.development.js.map +1 -1
- package/dist/api.cjs.production.min.js +1 -1
- package/dist/api.cjs.production.min.js.map +1 -1
- package/dist/api.esm.js +249 -25
- package/dist/api.esm.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/platforms/vtex/index.d.ts +5 -0
- package/dist/platforms/vtex/resolvers/skuVariations.d.ts +6 -0
- package/dist/platforms/vtex/utils/skuVariants.d.ts +15 -0
- package/package.json +3 -3
- package/src/__generated__/schema.ts +99 -0
- package/src/platforms/vtex/index.ts +2 -0
- package/src/platforms/vtex/resolvers/collection.ts +4 -1
- package/src/platforms/vtex/resolvers/productGroup.ts +4 -3
- package/src/platforms/vtex/resolvers/skuVariations.ts +47 -0
- package/src/platforms/vtex/resolvers/validateCart.ts +97 -40
- package/src/platforms/vtex/utils/skuVariants.ts +206 -0
- package/src/typeDefs/index.ts +3 -1
- package/src/typeDefs/productGroup.graphql +6 -0
- package/src/typeDefs/skuVariants.graphql +87 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { StoreProduct as StoreProductType } from '../../..'
|
|
2
|
+
import type { Product, Item } from '../clients/search/types/ProductSearchResult'
|
|
3
|
+
|
|
4
|
+
export type SkuVariants = StoreProductType[]
|
|
5
|
+
|
|
6
|
+
export type SkuVariantsByName = Record<string, Array<FormattedSkuVariant>>
|
|
7
|
+
|
|
8
|
+
type FormattedSkuVariant = {
|
|
9
|
+
alt: string
|
|
10
|
+
src: string
|
|
11
|
+
label: string
|
|
12
|
+
value: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function findSkuVariantImage(availableImages: Item['images']) {
|
|
16
|
+
return (
|
|
17
|
+
availableImages.find(
|
|
18
|
+
(imageProperties) => imageProperties.imageLabel === 'skuvariation'
|
|
19
|
+
) ?? availableImages[0]
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createSlugsMap(
|
|
24
|
+
variants: Item[],
|
|
25
|
+
dominantVariantName: string,
|
|
26
|
+
baseSlug: string
|
|
27
|
+
) {
|
|
28
|
+
/**
|
|
29
|
+
* Maps property value combinations to their respective SKU's slug. Enables
|
|
30
|
+
* us to retrieve the slug for the SKU that matches the currently selected
|
|
31
|
+
* variations in O(1) time.
|
|
32
|
+
*
|
|
33
|
+
* Example: `'Color-Red-Size-40': 'classic-shoes-37'`
|
|
34
|
+
*/
|
|
35
|
+
const slugsMap: Record<string, string> = {}
|
|
36
|
+
|
|
37
|
+
variants.forEach((variant) => {
|
|
38
|
+
const skuSpecificationProperties = variant.variations
|
|
39
|
+
|
|
40
|
+
if (skuSpecificationProperties.length === 0) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Make sure that the 'name-value' pair for the dominant variation
|
|
45
|
+
// is always the first one.
|
|
46
|
+
const dominantNameValue = `${dominantVariantName}-${
|
|
47
|
+
skuSpecificationProperties.find(
|
|
48
|
+
(variationDetails) => variationDetails.name === dominantVariantName
|
|
49
|
+
)?.values[0] ?? ''
|
|
50
|
+
}`
|
|
51
|
+
|
|
52
|
+
const skuVariantKey = skuSpecificationProperties.reduce((acc, property) => {
|
|
53
|
+
const shouldIgnore = property.name === dominantVariantName
|
|
54
|
+
|
|
55
|
+
if (shouldIgnore) {
|
|
56
|
+
return acc
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return acc + `-${property.name}-${property.values[0]}`
|
|
60
|
+
}, dominantNameValue)
|
|
61
|
+
|
|
62
|
+
slugsMap[skuVariantKey] = `${baseSlug}-${variant.itemId}`
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return slugsMap
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getActiveSkuVariations(variations: Item['variations']) {
|
|
69
|
+
const activeVariations: Record<string, string> = {}
|
|
70
|
+
|
|
71
|
+
variations.forEach((variation) => {
|
|
72
|
+
activeVariations[variation.name] = variation.values[0]
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return activeVariations
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getVariantsByName(
|
|
79
|
+
skuSpecifications: Product['skuSpecifications']
|
|
80
|
+
) {
|
|
81
|
+
const variants: Record<string, string[]> = {}
|
|
82
|
+
|
|
83
|
+
skuSpecifications?.forEach((specification) => {
|
|
84
|
+
variants[specification.field.originalName ?? specification.field.name] =
|
|
85
|
+
specification.values.map((value) => value.originalName ?? value.name)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return variants
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function compare(a: string, b: string) {
|
|
92
|
+
// Values are always represented as Strings, so we need to handle numbers
|
|
93
|
+
// in this special case.
|
|
94
|
+
if (!Number.isNaN(Number(a) - Number(b))) {
|
|
95
|
+
return Number(a) - Number(b)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (a < b) {
|
|
99
|
+
return -1
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (a > b) {
|
|
103
|
+
return 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function sortVariants(variantsByName: SkuVariantsByName) {
|
|
110
|
+
const sortedVariants = variantsByName
|
|
111
|
+
|
|
112
|
+
for (const variantProperty in variantsByName) {
|
|
113
|
+
variantsByName[variantProperty].sort((a, b) => compare(a.value, b.value))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return sortedVariants
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function getFormattedVariations(
|
|
120
|
+
variants: Item[],
|
|
121
|
+
dominantVariantName: string,
|
|
122
|
+
dominantVariantValue: string
|
|
123
|
+
) {
|
|
124
|
+
/**
|
|
125
|
+
* SKU options already formatted and indexed by their property name.
|
|
126
|
+
*
|
|
127
|
+
* Ex: {
|
|
128
|
+
* `Size`: [
|
|
129
|
+
* { label: '42', value: '42' },
|
|
130
|
+
* { label: '41', value: '41' },
|
|
131
|
+
* { label: '39', value: '39' },
|
|
132
|
+
* ]
|
|
133
|
+
* }
|
|
134
|
+
*/
|
|
135
|
+
const variantsByName: SkuVariantsByName = {}
|
|
136
|
+
|
|
137
|
+
const previouslySeenPropertyValues = new Set<string>()
|
|
138
|
+
|
|
139
|
+
variants.forEach((variant) => {
|
|
140
|
+
if (variant.variations.length === 0) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const variantImageToUse = findSkuVariantImage(variant.images)
|
|
145
|
+
|
|
146
|
+
const dominantVariantEntry = variant.variations.find(
|
|
147
|
+
(variation) => variation.name === dominantVariantName
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
const matchesDominantVariant =
|
|
151
|
+
dominantVariantEntry?.values[0] === dominantVariantValue
|
|
152
|
+
|
|
153
|
+
if (!matchesDominantVariant) {
|
|
154
|
+
const nameValueIdentifier = `${dominantVariantName}-${dominantVariantEntry?.values[0]}`
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
!dominantVariantEntry ||
|
|
158
|
+
previouslySeenPropertyValues.has(nameValueIdentifier)
|
|
159
|
+
) {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
previouslySeenPropertyValues.add(nameValueIdentifier)
|
|
164
|
+
|
|
165
|
+
const formattedVariant = {
|
|
166
|
+
src: variantImageToUse.imageUrl,
|
|
167
|
+
alt: variantImageToUse.imageLabel ?? '',
|
|
168
|
+
label: `${dominantVariantName}: ${dominantVariantEntry.values[0]}`,
|
|
169
|
+
value: dominantVariantEntry.values[0],
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (variantsByName[dominantVariantEntry.name]) {
|
|
173
|
+
variantsByName[dominantVariantEntry.name].push(formattedVariant)
|
|
174
|
+
} else {
|
|
175
|
+
variantsByName[dominantVariantEntry.name] = [formattedVariant]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
variant.variations.forEach((variationProperty) => {
|
|
182
|
+
const nameValueIdentifier = `${variationProperty.name}-${variationProperty.values[0]}`
|
|
183
|
+
|
|
184
|
+
if (previouslySeenPropertyValues.has(nameValueIdentifier)) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
previouslySeenPropertyValues.add(nameValueIdentifier)
|
|
189
|
+
|
|
190
|
+
const formattedVariant = {
|
|
191
|
+
src: variantImageToUse.imageUrl,
|
|
192
|
+
alt: variantImageToUse.imageLabel ?? '',
|
|
193
|
+
label: `${variationProperty.name}: ${variationProperty.values[0]}`,
|
|
194
|
+
value: variationProperty.values[0],
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (variantsByName[variationProperty.name]) {
|
|
198
|
+
variantsByName[variationProperty.name].push(formattedVariant)
|
|
199
|
+
} else {
|
|
200
|
+
variantsByName[variationProperty.name] = [formattedVariant]
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
return sortVariants(variantsByName)
|
|
206
|
+
}
|
package/src/typeDefs/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import Person from './person.graphql'
|
|
|
25
25
|
import ObjectOrString from './objectOrString.graphql'
|
|
26
26
|
import Session from './session.graphql'
|
|
27
27
|
import Newsletter from './newsletter.graphql'
|
|
28
|
+
import SkuVariants from './skuVariants.graphql'
|
|
28
29
|
|
|
29
30
|
export const typeDefs = [
|
|
30
31
|
Query,
|
|
@@ -51,7 +52,8 @@ export const typeDefs = [
|
|
|
51
52
|
Person,
|
|
52
53
|
ObjectOrString,
|
|
53
54
|
Session,
|
|
54
|
-
Newsletter
|
|
55
|
+
Newsletter,
|
|
56
|
+
SkuVariants,
|
|
55
57
|
]
|
|
56
58
|
.map(print)
|
|
57
59
|
.join('\n')
|
|
@@ -18,4 +18,10 @@ type StoreProductGroup {
|
|
|
18
18
|
Array of additional properties.
|
|
19
19
|
"""
|
|
20
20
|
additionalProperty: [StorePropertyValue!]!
|
|
21
|
+
"""
|
|
22
|
+
Object containing data structures to facilitate handling different SKU
|
|
23
|
+
variant properties. Specially useful for implementing SKU selection
|
|
24
|
+
components.
|
|
25
|
+
"""
|
|
26
|
+
skuVariants: SkuVariants
|
|
21
27
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
type SkuVariants {
|
|
2
|
+
"""
|
|
3
|
+
SKU property values for the current SKU.
|
|
4
|
+
"""
|
|
5
|
+
activeVariations: ActiveVariations
|
|
6
|
+
"""
|
|
7
|
+
All available options for each SKU variant property, indexed by their name.
|
|
8
|
+
"""
|
|
9
|
+
allVariantsByName: VariantsByName
|
|
10
|
+
"""
|
|
11
|
+
Maps property value combinations to their respective SKU's slug. Enables
|
|
12
|
+
us to retrieve the slug for the SKU that matches the currently selected
|
|
13
|
+
variations in O(1) time.
|
|
14
|
+
"""
|
|
15
|
+
slugsMap(dominantVariantName: String!): SlugsMap
|
|
16
|
+
"""
|
|
17
|
+
Available options for each varying SKU property, taking into account the
|
|
18
|
+
`dominantVariantName` property. Returns all available options for the
|
|
19
|
+
dominant property, and only options that can be combined with its current
|
|
20
|
+
value for other properties.
|
|
21
|
+
"""
|
|
22
|
+
availableVariations(dominantVariantName: String!): FormattedVariants
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
Example:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
'Color-Red-Size-40': 'classic-shoes-37'
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
scalar SlugsMap
|
|
35
|
+
"""
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
Color: 'Red', Size: '42'
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
"""
|
|
44
|
+
scalar ActiveVariations
|
|
45
|
+
"""
|
|
46
|
+
Example:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
Color: [ "Red", "Blue", "Green" ],
|
|
51
|
+
Size: [ "40", "41" ]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
scalar VariantsByName
|
|
56
|
+
"""
|
|
57
|
+
Example:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
Color: [
|
|
62
|
+
{
|
|
63
|
+
src: "https://storecomponents.vtexassets.com/...",
|
|
64
|
+
alt: "...",
|
|
65
|
+
label: "...",
|
|
66
|
+
value: "..."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
src: "https://storecomponents.vtexassets.com/...",
|
|
70
|
+
alt: "...",
|
|
71
|
+
label: "...",
|
|
72
|
+
value: "..."
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
Size: [
|
|
76
|
+
{
|
|
77
|
+
src: "https://storecomponents.vtexassets.com/...",
|
|
78
|
+
alt: "...",
|
|
79
|
+
label: "...",
|
|
80
|
+
value: "..."
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
"""
|
|
86
|
+
scalar FormattedVariants
|
|
87
|
+
|