@hanzo/commerce 6.1.8 → 6.1.9
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/components/buy/{_to_deprecate_select-category-item-card.tsx → _to_deprecate_select-family-item-card.tsx} +3 -3
- package/components/buy/{_to_deprecate_select-category-item-panel.tsx → _to_deprecate_select-family-item-panel.tsx} +20 -20
- package/components/buy/add-to-cart-widget.tsx +2 -2
- package/components/buy/buy-card.tsx +56 -54
- package/components/buy/buy-drawer.tsx +25 -11
- package/components/buy/carousel-buy-card.tsx +261 -0
- package/components/cart/cart-panel/index.tsx +1 -1
- package/components/checkout/payment-step-form/index.tsx +1 -1
- package/components/checkout/payment-step-form/methods/card.tsx +1 -1
- package/components/checkout/payment-step-form/methods/crypto.tsx +1 -1
- package/components/checkout/shipping-step-form.tsx +1 -1
- package/components/index.ts +4 -4
- package/components/item/product-card.tsx +2 -2
- package/components/select-family/family-carousel/family-slide.tsx +36 -0
- package/components/select-family/family-carousel/index.tsx +78 -0
- package/components/{level-nodes-widget → select-family/level-nodes-widget}/index.tsx +1 -1
- package/components/{level-nodes-widget → select-family/level-nodes-widget}/node-image.tsx +1 -1
- package/components/{select → select-product}/carousel-selector.tsx +8 -8
- package/components/{select → select-product}/image-selector.tsx +3 -3
- package/components/{select → select-product}/radio-selector.tsx +3 -3
- package/index.ts +8 -1
- package/package.json +2 -4
- package/service/context.tsx +4 -4
- package/service/impls/standalone/actual-line-item.ts +9 -9
- package/service/impls/standalone/index.ts +4 -4
- package/service/impls/standalone/standalone-service.ts +39 -53
- package/types/commerce-service.ts +18 -9
- package/types/{category.ts → family.ts} +2 -2
- package/types/index.ts +1 -1
- package/types/item-selector.ts +2 -2
- package/types/product.ts +4 -7
- package/util/buy-ui-conf.ts +2 -1
- package/util/sep.ts +5 -0
- package/util/use-sync-sku-param-w-current-item.ts +9 -8
- package/components/item/item-media.tsx +0 -51
|
@@ -11,7 +11,7 @@ import type { Product, LineItem, CommerceService } from '../../../types'
|
|
|
11
11
|
|
|
12
12
|
interface ActualLineItemSnapshot {
|
|
13
13
|
sku: string
|
|
14
|
-
|
|
14
|
+
familyId: string // helps impl of restoreFromSnapshot
|
|
15
15
|
title: string
|
|
16
16
|
price: number
|
|
17
17
|
quantity: number
|
|
@@ -28,8 +28,8 @@ class ActualLineItem
|
|
|
28
28
|
sku: string
|
|
29
29
|
fullTitle?: string
|
|
30
30
|
optionLabel: string
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
familyTitle: string
|
|
32
|
+
familyId: string
|
|
33
33
|
desc?: string
|
|
34
34
|
price: number
|
|
35
35
|
img?: ImageDef
|
|
@@ -42,8 +42,8 @@ class ActualLineItem
|
|
|
42
42
|
this.sku = prod.sku
|
|
43
43
|
this.fullTitle = prod.fullTitle
|
|
44
44
|
this.optionLabel = prod.optionLabel
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
45
|
+
this.familyTitle = prod.familyTitle
|
|
46
|
+
this.familyId = prod.familyId
|
|
47
47
|
this.desc = prod.desc
|
|
48
48
|
this.price = prod.price
|
|
49
49
|
this.img = prod.img
|
|
@@ -67,7 +67,7 @@ class ActualLineItem
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
get title(): string {
|
|
70
|
-
return this.fullTitle ? this.fullTitle : (this.
|
|
70
|
+
return this.fullTitle ? this.fullTitle : (this.familyTitle + ', ' + this.optionLabel)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
takeSnapshot = (cmmc: CommerceService): ActualLineItemSnapshot => {
|
|
@@ -75,11 +75,11 @@ class ActualLineItem
|
|
|
75
75
|
const title = this.fullTitle ?
|
|
76
76
|
this.fullTitle
|
|
77
77
|
:
|
|
78
|
-
((cmmc.
|
|
78
|
+
((cmmc.getFamily(this.familyId)?.title ?? this.familyTitle) + ', ' + this.optionLabel)
|
|
79
79
|
|
|
80
80
|
return({
|
|
81
81
|
sku: this.sku,
|
|
82
|
-
|
|
82
|
+
familyId: this.familyId,
|
|
83
83
|
title,
|
|
84
84
|
price: this.price,
|
|
85
85
|
quantity: this.qu,
|
|
@@ -107,7 +107,7 @@ class ActualLineItem
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
inFamily(id: string): boolean {
|
|
111
111
|
// TODO: will break for level one (which is ok for lux, but not generally)
|
|
112
112
|
return this.sku.includes(`-${id}-`)
|
|
113
113
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { enableStaticRendering } from 'mobx-react-lite'
|
|
2
2
|
|
|
3
|
-
import type { CommerceService,
|
|
3
|
+
import type { CommerceService, Family, ProductTreeNode } from '../../../types'
|
|
4
4
|
import StandaloneService, {type StandaloneServiceOptions} from './standalone-service'
|
|
5
5
|
|
|
6
6
|
import { readSnapshot, listenAndWriteSnapshots } from './localStorage'
|
|
@@ -18,7 +18,7 @@ const _log = (s: string) => {
|
|
|
18
18
|
let instance: StandaloneService | undefined = undefined
|
|
19
19
|
|
|
20
20
|
export const getInstance = (
|
|
21
|
-
|
|
21
|
+
families: Family[],
|
|
22
22
|
rootNode: ProductTreeNode,
|
|
23
23
|
options?: StandaloneServiceOptions
|
|
24
24
|
): CommerceService => {
|
|
@@ -30,7 +30,7 @@ export const getInstance = (
|
|
|
30
30
|
if (typeof window === "undefined") {
|
|
31
31
|
//_log("NEW INSTANCE FOR SERVER")
|
|
32
32
|
return new StandaloneService(
|
|
33
|
-
|
|
33
|
+
families,
|
|
34
34
|
rootNode,
|
|
35
35
|
options
|
|
36
36
|
)
|
|
@@ -41,7 +41,7 @@ export const getInstance = (
|
|
|
41
41
|
//_log("NEW INSTANCE FOR CLIENT")
|
|
42
42
|
const snapShot = readSnapshot()
|
|
43
43
|
instance = new StandaloneService(
|
|
44
|
-
|
|
44
|
+
families,
|
|
45
45
|
rootNode,
|
|
46
46
|
options,
|
|
47
47
|
snapShot
|
|
@@ -11,7 +11,7 @@ import { computedFn } from 'mobx-utils'
|
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
CommerceService,
|
|
14
|
-
|
|
14
|
+
Family,
|
|
15
15
|
LineItem,
|
|
16
16
|
SelectedPaths,
|
|
17
17
|
ProductTreeNode,
|
|
@@ -25,8 +25,7 @@ import {
|
|
|
25
25
|
} from './orders'
|
|
26
26
|
|
|
27
27
|
import ActualLineItem, { type ActualLineItemSnapshot } from './actual-line-item'
|
|
28
|
-
|
|
29
|
-
const SEP = '-'
|
|
28
|
+
import SEP from '../../../util/sep'
|
|
30
29
|
|
|
31
30
|
type StandaloneServiceOptions = {
|
|
32
31
|
levelZeroPrefix?: string
|
|
@@ -41,7 +40,7 @@ interface StandaloneServiceSnapshot {
|
|
|
41
40
|
class StandaloneService
|
|
42
41
|
implements CommerceService
|
|
43
42
|
{
|
|
44
|
-
private
|
|
43
|
+
private _familyMap = new Map<string, Family>()
|
|
45
44
|
private _rootNode: ProductTreeNode
|
|
46
45
|
private _selectedPaths: SelectedPaths = {}
|
|
47
46
|
private _promo: Promo | null = null
|
|
@@ -50,7 +49,7 @@ class StandaloneService
|
|
|
50
49
|
private _currentItem: ActualLineItem | undefined = undefined
|
|
51
50
|
|
|
52
51
|
constructor(
|
|
53
|
-
|
|
52
|
+
families: Family[],
|
|
54
53
|
rootNode: ProductTreeNode,
|
|
55
54
|
options: StandaloneServiceOptions,
|
|
56
55
|
serviceSnapshot?: StandaloneServiceSnapshot,
|
|
@@ -59,7 +58,7 @@ class StandaloneService
|
|
|
59
58
|
this._rootNode = rootNode
|
|
60
59
|
this._options = options
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
families.forEach((c) => {
|
|
63
62
|
c.products = c.products.map((p) => {
|
|
64
63
|
if (serviceSnapshot) {
|
|
65
64
|
const itemSnapshot = serviceSnapshot.items.find((is) => (is.sku === p.sku))
|
|
@@ -69,7 +68,7 @@ class StandaloneService
|
|
|
69
68
|
}
|
|
70
69
|
return new ActualLineItem(p)
|
|
71
70
|
})
|
|
72
|
-
this.
|
|
71
|
+
this._familyMap.set(c.id, c)
|
|
73
72
|
})
|
|
74
73
|
|
|
75
74
|
makeObservable<
|
|
@@ -90,7 +89,7 @@ class StandaloneService
|
|
|
90
89
|
promoAppliedCartTotal: computed,
|
|
91
90
|
cartEmpty: computed,
|
|
92
91
|
selectedItems: computed,
|
|
93
|
-
|
|
92
|
+
selectedFamilies: computed,
|
|
94
93
|
hasSelection: computed,
|
|
95
94
|
setCurrentItem: action,
|
|
96
95
|
currentItem: computed,
|
|
@@ -102,12 +101,12 @@ class StandaloneService
|
|
|
102
101
|
})
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
return this.
|
|
104
|
+
getFamily(id: string): Family | undefined {
|
|
105
|
+
return this._familyMap.get(id)
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
getNodeAtPath(skuPath: string): ProductTreeNode | undefined {
|
|
110
|
-
const toks = skuPath.split(SEP)
|
|
109
|
+
const toks = skuPath.split(SEP.TOK)
|
|
111
110
|
let level = 1
|
|
112
111
|
let desc: ProductTreeNode | undefined = this._rootNode
|
|
113
112
|
do {
|
|
@@ -170,8 +169,8 @@ class StandaloneService
|
|
|
170
169
|
|
|
171
170
|
get cartItems(): LineItem[] {
|
|
172
171
|
let result: LineItem[] = []
|
|
173
|
-
this.
|
|
174
|
-
result = [...result, ...(
|
|
172
|
+
this._familyMap.forEach((fam) => {
|
|
173
|
+
result = [...result, ...(fam.products as LineItem[]).filter((item) => (item.isInCart))]
|
|
175
174
|
})
|
|
176
175
|
return result.sort((it1, it2) => ((it1 as ActualLineItem).timeAdded - (it2 as ActualLineItem).timeAdded))
|
|
177
176
|
}
|
|
@@ -238,46 +237,33 @@ class StandaloneService
|
|
|
238
237
|
|
|
239
238
|
setCurrentItem(skuToFind: string | undefined): boolean {
|
|
240
239
|
|
|
241
|
-
const logMe = (s: string) => {
|
|
242
|
-
//if (skuToFind?.startsWith('LXM-CR-E')) {
|
|
243
|
-
//console.log(s)
|
|
244
|
-
//}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
240
|
if (skuToFind === undefined || skuToFind.length === 0) {
|
|
248
241
|
this._currentItem = undefined
|
|
249
242
|
return true
|
|
250
243
|
}
|
|
251
|
-
|
|
252
|
-
logMe("SETTING ITEM: " + skuToFind)
|
|
253
|
-
|
|
254
244
|
// self calling function
|
|
255
245
|
this._currentItem = ((): ActualLineItem | undefined => {
|
|
256
246
|
|
|
257
|
-
const
|
|
258
|
-
if (this.
|
|
259
|
-
for (let
|
|
260
|
-
|
|
261
|
-
const foundItem =
|
|
247
|
+
const familiesTried: string[] = []
|
|
248
|
+
if (this.selectedFamilies && this.selectedFamilies.length > 0) {
|
|
249
|
+
for (let family of this.selectedFamilies) {
|
|
250
|
+
familiesTried.push(family.id)
|
|
251
|
+
const foundItem = family.products.find((p) => (p.sku === skuToFind))
|
|
262
252
|
if (foundItem) {
|
|
263
|
-
logMe("FOUND 1st LOOP")
|
|
264
253
|
return foundItem as ActualLineItem
|
|
265
254
|
}
|
|
266
255
|
}
|
|
267
256
|
}
|
|
268
|
-
for( const [
|
|
269
|
-
if (
|
|
270
|
-
const foundItem =
|
|
257
|
+
for( const [familyId, family] of this._familyMap.entries()) {
|
|
258
|
+
if (familiesTried.includes(familyId)) continue
|
|
259
|
+
const foundItem = family.products.find((p) => (p.sku === skuToFind)) as ActualLineItem | undefined
|
|
271
260
|
if (foundItem) {
|
|
272
|
-
logMe("FOUND 2nd LOOP")
|
|
273
261
|
return foundItem as ActualLineItem
|
|
274
262
|
}
|
|
275
263
|
}
|
|
276
|
-
logMe("NOT FOUND")
|
|
277
264
|
return undefined
|
|
278
265
|
})();
|
|
279
266
|
|
|
280
|
-
logMe("CURRENT ITEM SET TO: " + this._currentItem?.sku)
|
|
281
267
|
return !!this._currentItem
|
|
282
268
|
}
|
|
283
269
|
|
|
@@ -290,15 +276,15 @@ class StandaloneService
|
|
|
290
276
|
return this._currentItem
|
|
291
277
|
}
|
|
292
278
|
|
|
293
|
-
selectPaths(sel: SelectedPaths):
|
|
279
|
+
selectPaths(sel: SelectedPaths): Family[] {
|
|
294
280
|
runInAction (() => {
|
|
295
281
|
this._selectedPaths = this._processAndValidate(sel)
|
|
296
282
|
})
|
|
297
|
-
return this.
|
|
283
|
+
return this.selectedFamilies
|
|
298
284
|
}
|
|
299
285
|
|
|
300
|
-
selectPath(skuPath: string):
|
|
301
|
-
const toks = skuPath.split(SEP)
|
|
286
|
+
selectPath(skuPath: string): Family[] {
|
|
287
|
+
const toks = skuPath.split(SEP.TOK)
|
|
302
288
|
const highestLevel = toks.length - 1
|
|
303
289
|
const fsv: SelectedPaths = {}
|
|
304
290
|
for (let level = 1; level <= highestLevel; level++ ) {
|
|
@@ -315,14 +301,14 @@ class StandaloneService
|
|
|
315
301
|
return result
|
|
316
302
|
}
|
|
317
303
|
|
|
318
|
-
get
|
|
304
|
+
get selectedFamilies(): Family[] {
|
|
319
305
|
if (Object.keys(toJS(this._selectedPaths)).length === 0) {
|
|
320
306
|
// FacetsDesc have never been set or unset, so cannot evaluate them
|
|
321
307
|
return []
|
|
322
308
|
}
|
|
323
309
|
|
|
324
310
|
return this._rootNode.subNodes!.reduce(
|
|
325
|
-
(acc:
|
|
311
|
+
(acc: Family[], subFacet: ProductTreeNode) => (
|
|
326
312
|
// Pass the root token as a one member array
|
|
327
313
|
this._reduceNode([this._rootNode.skuToken], acc, subFacet)
|
|
328
314
|
),
|
|
@@ -330,7 +316,7 @@ class StandaloneService
|
|
|
330
316
|
)
|
|
331
317
|
}
|
|
332
318
|
|
|
333
|
-
private _reduceNode(parentPath: string[], acc:
|
|
319
|
+
private _reduceNode(parentPath: string[], acc: Family[], node: ProductTreeNode): Family[] {
|
|
334
320
|
const path = [...parentPath, node.skuToken] // Don't mutate original please :)
|
|
335
321
|
const level = path.length - 1
|
|
336
322
|
// If there is no token array supplied for this level,
|
|
@@ -350,11 +336,11 @@ class StandaloneService
|
|
|
350
336
|
, acc)
|
|
351
337
|
}
|
|
352
338
|
// Process leaf
|
|
353
|
-
const
|
|
354
|
-
if (!
|
|
355
|
-
throw new Error("
|
|
339
|
+
const fam = this._familyMap.get(path.join(SEP.TOK))
|
|
340
|
+
if (!fam) {
|
|
341
|
+
throw new Error("selectedFamilies WTF?!" + path.join(SEP.TOK))
|
|
356
342
|
}
|
|
357
|
-
acc.push(
|
|
343
|
+
acc.push(fam)
|
|
358
344
|
}
|
|
359
345
|
return acc
|
|
360
346
|
}
|
|
@@ -389,16 +375,16 @@ class StandaloneService
|
|
|
389
375
|
return []
|
|
390
376
|
}
|
|
391
377
|
|
|
392
|
-
return this.
|
|
393
|
-
(allProducts,
|
|
378
|
+
return this.selectedFamilies.reduce(
|
|
379
|
+
(allProducts, fam) => ([...allProducts, ...(fam.products as LineItem[])]), [] as LineItem[])
|
|
394
380
|
}
|
|
395
381
|
|
|
396
382
|
get hasSelection(): boolean {
|
|
397
|
-
return this.
|
|
383
|
+
return this.selectedFamilies.length > 0
|
|
398
384
|
}
|
|
399
385
|
|
|
400
|
-
|
|
401
|
-
const c = this.
|
|
386
|
+
getFamilySubtotal(familyId: string): number {
|
|
387
|
+
const c = this._familyMap.get(familyId)!
|
|
402
388
|
return (c.products as LineItem[]).reduce(
|
|
403
389
|
// avoid floating point bs around zero
|
|
404
390
|
(total, item) => (item.quantity > 0 ? total + item.price * item.quantity : total),
|
|
@@ -406,9 +392,9 @@ class StandaloneService
|
|
|
406
392
|
)
|
|
407
393
|
}
|
|
408
394
|
|
|
409
|
-
|
|
410
|
-
const spec = this.
|
|
411
|
-
console.log('NUM
|
|
395
|
+
debug_getSelectedFamilies = () => {
|
|
396
|
+
const spec = this.selectedFamilies
|
|
397
|
+
console.log('NUM SEL FAMS: ', spec.length)
|
|
412
398
|
if (spec.length > 0) {
|
|
413
399
|
console.log('IDS: ', (spec.map((c) => (c.id))).join(', '))
|
|
414
400
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LineItem, ObsLineItemRef } from './line-item'
|
|
2
2
|
import type { ProductTreeNode, SelectedPaths } from './tree-node'
|
|
3
|
-
import type
|
|
3
|
+
import type Family from './family'
|
|
4
4
|
import type Promo from './promo'
|
|
5
5
|
|
|
6
6
|
interface CommerceService extends ObsLineItemRef {
|
|
@@ -21,7 +21,7 @@ interface CommerceService extends ObsLineItemRef {
|
|
|
21
21
|
/** returns the price with promo applied, of undefined if no promo or promo does not apply */
|
|
22
22
|
itemPromoPrice(item: LineItem): number | undefined
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
getFamilySubtotal(familyId: string): number
|
|
25
25
|
|
|
26
26
|
createOrder(email: string, name?: string): Promise<string | undefined>
|
|
27
27
|
updateOrderShippingInfo(orderId: string, shippingInfo: any): Promise<void>
|
|
@@ -32,20 +32,28 @@ interface CommerceService extends ObsLineItemRef {
|
|
|
32
32
|
* If a level is selected as [], nothing will be selected.
|
|
33
33
|
* If a level is missing (undefined), everything at that level is selected
|
|
34
34
|
*
|
|
35
|
-
* This selects one or more
|
|
35
|
+
* This selects one or more Family's, and all the LineItem's in them.
|
|
36
36
|
*
|
|
37
|
-
* An empty value object selects all
|
|
37
|
+
* An empty value object selects all Family's and all LineItem's,
|
|
38
38
|
* */
|
|
39
|
-
selectPaths(value: SelectedPaths):
|
|
40
|
-
selectPath(skuPath: string):
|
|
39
|
+
selectPaths(value: SelectedPaths): Family[]
|
|
40
|
+
selectPath(skuPath: string): Family[]
|
|
41
41
|
|
|
42
|
+
/*
|
|
43
|
+
peekPath(skuPath: string): {
|
|
44
|
+
family: Family | undefined
|
|
45
|
+
siblingFamilies: Family[] | undefined
|
|
46
|
+
categoryNode: ProductTreeNode | undefined
|
|
47
|
+
isSku: boolean
|
|
48
|
+
}
|
|
49
|
+
*/
|
|
42
50
|
get selectedPaths(): SelectedPaths // returns a copy
|
|
43
51
|
|
|
44
52
|
get selectedItems(): LineItem[]
|
|
45
|
-
get
|
|
53
|
+
get selectedFamilies(): Family[]
|
|
46
54
|
get hasSelection(): boolean
|
|
47
55
|
|
|
48
|
-
/** Whether this path defines a
|
|
56
|
+
/** Whether this path defines a Family, or if it has further levels */
|
|
49
57
|
getNodeAtPath(skuPath: string): ProductTreeNode | undefined
|
|
50
58
|
|
|
51
59
|
/**
|
|
@@ -72,10 +80,11 @@ interface CommerceService extends ObsLineItemRef {
|
|
|
72
80
|
* */
|
|
73
81
|
get currentItem(): LineItem | undefined
|
|
74
82
|
|
|
75
|
-
|
|
83
|
+
getFamily(id: string): Family | undefined
|
|
76
84
|
|
|
77
85
|
}
|
|
78
86
|
|
|
87
|
+
|
|
79
88
|
export {
|
|
80
89
|
type CommerceService as default
|
|
81
90
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ImageDef } from '@hanzo/ui/types'
|
|
2
2
|
import type Product from './product'
|
|
3
3
|
|
|
4
|
-
interface
|
|
4
|
+
interface Family {
|
|
5
5
|
id: string // LXB-AU-B
|
|
6
6
|
title: string // Minted Bar, Lux Elite Card
|
|
7
7
|
titleShort?: string // Elite
|
|
@@ -13,5 +13,5 @@ interface Category {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export {
|
|
16
|
-
type
|
|
16
|
+
type Family as default
|
|
17
17
|
}
|
package/types/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
export type { default as
|
|
2
|
+
export type { default as Family } from './family'
|
|
3
3
|
export type { default as CommerceService } from './commerce-service'
|
|
4
4
|
export type { default as Product } from './product'
|
|
5
5
|
export type { default as Promo } from './promo'
|
package/types/item-selector.ts
CHANGED
|
@@ -17,12 +17,12 @@ interface _ItemSelectorCompProps {
|
|
|
17
17
|
/** List selectors will scroll. Used internally */
|
|
18
18
|
scrollList: boolean
|
|
19
19
|
/**
|
|
20
|
-
* Whether the item label includes the
|
|
20
|
+
* Whether the item label includes the Family name.
|
|
21
21
|
* eg, 'Minted Bar, 1oz' vs '1oz'
|
|
22
22
|
* default: true in 'allVariants' mode and false otherwise.
|
|
23
23
|
* This overrides
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
showFamily?: boolean
|
|
26
26
|
/**
|
|
27
27
|
* Show the current item quantity along with title and price.
|
|
28
28
|
* default: false
|
package/types/product.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MediaStackDef } from '@hanzo/ui/types'
|
|
2
2
|
|
|
3
|
-
interface Product {
|
|
3
|
+
interface Product extends MediaStackDef {
|
|
4
4
|
id: string // DB index // not a logical aspect of our domain. may not be necessary at all
|
|
5
5
|
sku: string // human visible on orders etc.
|
|
6
6
|
fullTitle?: string
|
|
7
7
|
optionLabel: string
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
familyId: string // skuPath, eg LXB-AU-B
|
|
9
|
+
familyTitle: string
|
|
10
10
|
desc?: string
|
|
11
11
|
price: number
|
|
12
|
-
img?: ImageDef // if undefined: (category's img exists) ? (use it) : (use generic placeholder)
|
|
13
|
-
animation?: string // spline scene url
|
|
14
|
-
video?: VideoDef
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
export {
|
package/util/buy-ui-conf.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BuyUISpec } from '../types'
|
|
2
|
+
import SEP from './sep'
|
|
2
3
|
|
|
3
4
|
const map = new Map<string, BuyUISpec>([
|
|
4
5
|
['LXM_AU', {
|
|
@@ -29,6 +30,6 @@ const map = new Map<string, BuyUISpec>([
|
|
|
29
30
|
])
|
|
30
31
|
|
|
31
32
|
export default (skuPath: string): BuyUISpec | undefined => {
|
|
32
|
-
const key = skuPath.split(
|
|
33
|
+
const key = skuPath.split(SEP.TOK).slice(0, 2).join(SEP.TOK)
|
|
33
34
|
return map.get(key)
|
|
34
35
|
}
|
package/util/sep.ts
ADDED
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import { useCommerce } from '../service/context'
|
|
12
12
|
import type { SelectedPaths } from '../types'
|
|
13
|
+
import SEP from './sep'
|
|
13
14
|
|
|
14
15
|
const PLEASE_SELECT_FACETS = 'Please select an option from each group.'
|
|
15
16
|
|
|
16
17
|
const useSyncSkuParamWithCurrentItem = (
|
|
17
|
-
|
|
18
|
+
familyLevel: number,
|
|
18
19
|
setMessage: (m: string | undefined) => void,
|
|
19
20
|
setLoading?: (l: boolean) => void
|
|
20
21
|
) => {
|
|
@@ -32,14 +33,14 @@ const useSyncSkuParamWithCurrentItem = (
|
|
|
32
33
|
useEffect(() => {
|
|
33
34
|
|
|
34
35
|
return reaction(() => ({
|
|
35
|
-
specifiedCat: cmmc.
|
|
36
|
+
specifiedCat: cmmc.selectedFamilies.length === 1 ? cmmc.selectedFamilies[0] : undefined,
|
|
36
37
|
currentItem: cmmc.currentItem
|
|
37
38
|
}),
|
|
38
39
|
({specifiedCat, currentItem}) => {
|
|
39
|
-
//console.log("REACTION: " + `CAT ID: ${
|
|
40
|
+
//console.log("REACTION: " + `CAT ID: ${family?.id} SKU: ${item?.sku}`)
|
|
40
41
|
if (currentItem) {
|
|
41
42
|
// if we set the currentItem w sku, then user selects other facets
|
|
42
|
-
if (specifiedCat && currentItem.
|
|
43
|
+
if (specifiedCat && currentItem.familyId != specifiedCat.id ) {
|
|
43
44
|
cmmc.setCurrentItem(specifiedCat.products[0].sku)
|
|
44
45
|
}
|
|
45
46
|
setSkuParam(cmmc.currentItem!.sku)
|
|
@@ -53,11 +54,11 @@ const useSyncSkuParamWithCurrentItem = (
|
|
|
53
54
|
|
|
54
55
|
useEffect(() => {
|
|
55
56
|
|
|
56
|
-
const
|
|
57
|
-
const toks: string[] = sku.split(
|
|
57
|
+
const setCurrentFamilyFromSku = (sku: string) => {
|
|
58
|
+
const toks: string[] = sku.split(SEP.TOK)
|
|
58
59
|
const fv: SelectedPaths = {}
|
|
59
60
|
// TODO: confirm that extra trailing nonsense tokens won't break selectPaths()
|
|
60
|
-
for (let i = 1; i <
|
|
61
|
+
for (let i = 1; i < familyLevel; i++) {
|
|
61
62
|
if (i in toks) {
|
|
62
63
|
fv[i] = [toks[i]]
|
|
63
64
|
}
|
|
@@ -73,7 +74,7 @@ const useSyncSkuParamWithCurrentItem = (
|
|
|
73
74
|
}
|
|
74
75
|
setAddParam(false)
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
+
setCurrentFamilyFromSku(skuParam)
|
|
77
78
|
setMessage(undefined)
|
|
78
79
|
}
|
|
79
80
|
else {
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
import Spline from '@splinetool/react-spline'
|
|
4
|
-
|
|
5
|
-
import { Image } from '@hanzo/ui/primitives'
|
|
6
|
-
import { cn } from '@hanzo/ui/util'
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
VideoBlockComponent,
|
|
10
|
-
type Block,
|
|
11
|
-
type VideoBlock
|
|
12
|
-
} from '@hanzo/ui/blocks'
|
|
13
|
-
import type { Product } from '../../types'
|
|
14
|
-
|
|
15
|
-
const ItemMedia: React.FC<{
|
|
16
|
-
item: Product
|
|
17
|
-
constrainTo?: {w: number, h: number}
|
|
18
|
-
clx?: string
|
|
19
|
-
}> = ({
|
|
20
|
-
item: {img, video, animation},
|
|
21
|
-
constrainTo: cnst = {w: 250, h: 250},
|
|
22
|
-
clx=''
|
|
23
|
-
}) => {
|
|
24
|
-
|
|
25
|
-
// Order of precedence: 3D > MP4 > Image
|
|
26
|
-
return animation ? (
|
|
27
|
-
<Spline
|
|
28
|
-
scene={animation}
|
|
29
|
-
className={cn(clx, 'pointer-events-none')}
|
|
30
|
-
style={{
|
|
31
|
-
// // !aspect-[12/10]
|
|
32
|
-
width: (6/5 * (typeof cnst.h === 'number' ? cnst.h as number : parseInt(cnst.h as string)) ),
|
|
33
|
-
height: cnst.h
|
|
34
|
-
}}
|
|
35
|
-
/>
|
|
36
|
-
) : video ? (
|
|
37
|
-
<VideoBlockComponent
|
|
38
|
-
constrainTo={cnst}
|
|
39
|
-
block={{blockType: 'video', ...video} satisfies VideoBlock as Block}
|
|
40
|
-
className={clx}
|
|
41
|
-
/>
|
|
42
|
-
) : (img &&
|
|
43
|
-
<Image
|
|
44
|
-
def={img}
|
|
45
|
-
constrainTo={cnst}
|
|
46
|
-
className={cn('m-auto', clx)}
|
|
47
|
-
/>
|
|
48
|
-
) // TODO: return placeholder
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export default ItemMedia
|