@hanzo/commerce 5.1.2 → 6.0.0

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.
Files changed (59) hide show
  1. package/components/{buy-item/select-category-item-card.tsx → buy/_to_deprecate_select-category-item-card.tsx} +20 -31
  2. package/components/{select-category-item-panel.tsx → buy/_to_deprecate_select-category-item-panel.tsx} +12 -20
  3. package/components/{buy-item/buy-item-popup.tsx → buy/_unused_buy-popup.tsx_} +4 -4
  4. package/components/{add-to-cart-widget.tsx → buy/add-to-cart-widget.tsx} +5 -3
  5. package/components/buy/buy-button.tsx +31 -0
  6. package/components/buy/buy-card.tsx +169 -0
  7. package/components/buy/buy-drawer.tsx +78 -0
  8. package/components/buy/buy-trigger-wrapper.tsx +20 -0
  9. package/components/{cart-accordian.tsx → cart/cart-accordian.tsx} +2 -1
  10. package/components/{cart-panel → cart/cart-panel}/cart-line-item.tsx +28 -5
  11. package/components/{cart-panel → cart/cart-panel}/index.tsx +9 -5
  12. package/components/{cart-panel → cart/cart-panel}/promo-code.tsx +3 -3
  13. package/components/{payment-step-form → checkout/payment-step-form}/contact-form.tsx +1 -1
  14. package/components/{payment-step-form → checkout/payment-step-form}/index.tsx +3 -3
  15. package/components/{payment-step-form → checkout/payment-step-form}/methods/bank-transfer.tsx +1 -1
  16. package/components/{payment-step-form → checkout/payment-step-form}/methods/card.tsx +4 -4
  17. package/components/{payment-step-form → checkout/payment-step-form}/methods/crypto.tsx +3 -3
  18. package/components/{payment-step-form → checkout/payment-step-form}/methods/index.ts +1 -1
  19. package/components/{shipping-step-form.tsx → checkout/shipping-step-form.tsx} +4 -4
  20. package/components/facet-values-widget/facet-image.tsx +2 -2
  21. package/components/facet-values-widget/index.tsx +13 -12
  22. package/components/index.ts +19 -17
  23. package/components/item/item-media.tsx +58 -0
  24. package/components/{product-card.tsx → item/product-card.tsx} +5 -7
  25. package/components/select/carousel-selector.tsx +64 -0
  26. package/components/select/image-selector.tsx +125 -0
  27. package/components/select/radio-selector.tsx +66 -0
  28. package/package.json +2 -1
  29. package/service/context.tsx +4 -4
  30. package/service/impls/standalone/index.ts +4 -4
  31. package/service/impls/standalone/standalone-service.ts +74 -62
  32. package/types/commerce-service.ts +20 -16
  33. package/types/index.ts +2 -2
  34. package/types/item-selector.ts +12 -5
  35. package/types/product.ts +1 -0
  36. package/types/tree-node.ts +24 -0
  37. package/util/index.ts +9 -9
  38. package/util/use-sync-sku-param-w-current-item.ts +5 -5
  39. package/components/buy-item/buy-item-button-wrapper.tsx +0 -27
  40. package/components/buy-item/buy-item-button.tsx +0 -34
  41. package/components/buy-item/buy-item-card.tsx +0 -92
  42. package/components/buy-item/buy-item-mobile-drawer.tsx +0 -54
  43. package/components/buy-item/select-category-and-item-widget.tsx +0 -83
  44. package/components/category-item-radio-selector.tsx +0 -55
  45. package/components/category-item-scroll-selector.tsx +0 -40
  46. package/components/item-carousel.tsx +0 -80
  47. package/types/facet.ts +0 -35
  48. /package/components/{cart-icon.tsx → cart/cart-icon.tsx} +0 -0
  49. /package/components/{payment-step-form → checkout/payment-step-form}/card-icon-row.tsx +0 -0
  50. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/amex.tsx +0 -0
  51. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/diners-club.tsx +0 -0
  52. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/discover.tsx +0 -0
  53. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/jcb.tsx +0 -0
  54. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/mastercard.tsx +0 -0
  55. /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/visa.tsx +0 -0
  56. /package/components/{payment-step-form → checkout/payment-step-form}/cc-button.tsx +0 -0
  57. /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/btc.tsx +0 -0
  58. /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/eth.tsx +0 -0
  59. /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/usdt.tsx +0 -0
@@ -13,8 +13,8 @@ import type {
13
13
  CommerceService,
14
14
  Category,
15
15
  LineItem,
16
- FacetsValue,
17
- FacetValueDesc,
16
+ SelectedPaths,
17
+ ProductTreeNode,
18
18
  Promo
19
19
  } from '../../../types'
20
20
 
@@ -42,8 +42,8 @@ class StandaloneService
42
42
  implements CommerceService
43
43
  {
44
44
  private _categoryMap = new Map<string, Category>()
45
- private _rootFacet: FacetValueDesc
46
- private _selectedFacets: FacetsValue = {}
45
+ private _rootNode: ProductTreeNode
46
+ private _selectedPaths: SelectedPaths = {}
47
47
  private _promo: Promo | null = null
48
48
 
49
49
  private _options : StandaloneServiceOptions
@@ -51,12 +51,12 @@ class StandaloneService
51
51
 
52
52
  constructor(
53
53
  categories: Category[],
54
- rootFacet: FacetValueDesc,
54
+ rootNode: ProductTreeNode,
55
55
  options: StandaloneServiceOptions,
56
56
  serviceSnapshot?: StandaloneServiceSnapshot,
57
57
  ) {
58
58
 
59
- this._rootFacet = rootFacet
59
+ this._rootNode = rootNode
60
60
  this._options = options
61
61
 
62
62
  categories.forEach((c) => {
@@ -74,11 +74,11 @@ class StandaloneService
74
74
 
75
75
  makeObservable<
76
76
  StandaloneService,
77
- '_selectedFacets' |
77
+ '_selectedPaths' |
78
78
  '_currentItem' |
79
79
  '_promo'
80
80
  >(this, {
81
- _selectedFacets : observable.deep,
81
+ _selectedPaths : observable.deep,
82
82
  _currentItem: observable,
83
83
  _promo: observable,
84
84
  })
@@ -89,54 +89,52 @@ class StandaloneService
89
89
  cartTotal: computed,
90
90
  promoAppliedCartTotal: computed,
91
91
  cartEmpty: computed,
92
- specifiedItems: computed,
93
- specifiedCategories: computed,
92
+ selectedItems: computed,
93
+ selectedCategories: computed,
94
94
  setCurrentItem: action,
95
95
  currentItem: computed,
96
96
  item: computed,
97
- facetsValue: computed,
97
+ selectedPaths: computed,
98
98
  appliedPromo: computed,
99
99
  setAppliedPromo: action,
100
- /* NOT setFacets. It implements it's action mechanism */
100
+ /* NOT selectPaths. It implements it's action mechanism */
101
101
  })
102
102
  }
103
103
 
104
-
105
104
  getCategory(id: string): Category | undefined {
106
105
  return this._categoryMap.get(id)
107
106
  }
108
107
 
109
- getFacetValuesAtSkuPath(skuPath: string): FacetValueDesc[] | undefined {
108
+ getNodeAtPath(skuPath: string): ProductTreeNode | undefined {
110
109
  const toks = skuPath.split(SEP)
111
110
  let level = 1
112
- let valuesAtLevel: FacetValueDesc[] | undefined = this._rootFacet.sub
111
+ let desc: ProductTreeNode | undefined = this._rootNode
113
112
  do {
114
- const fvalue = valuesAtLevel!.find((vf) => (vf.value === toks[level]))
115
- valuesAtLevel = fvalue ? fvalue.sub : undefined
113
+ desc = desc!.subNodes?.find((vf) => (vf.skuToken === toks[level]))
116
114
  level++
117
115
  }
118
- while (valuesAtLevel && (level < toks.length))
119
- return level === toks.length ? valuesAtLevel : undefined
116
+ while (desc && (level < toks.length))
117
+ return level === toks.length ? desc : undefined
120
118
  }
121
119
 
122
- getFacetValuesSpecified = computedFn((level: number): FacetValueDesc[] | undefined => {
120
+ getSelectedNodesAtLevel = computedFn((level: number): ProductTreeNode[] | undefined => {
123
121
 
124
122
  let lvl = 1
125
- let valuesAtLevel: FacetValueDesc[] | undefined = this._rootFacet.sub
123
+ let valuesAtLevel: ProductTreeNode[] | undefined = this._rootNode.subNodes
126
124
 
127
125
  do {
128
- let selectedAtLevel: FacetValueDesc[] | undefined = undefined
126
+ let selectedAtLevel: ProductTreeNode[] | undefined = undefined
129
127
  // If not specified, assume all
130
- if (lvl in this._selectedFacets) {
131
- selectedAtLevel = valuesAtLevel!.filter((fv) => (this._selectedFacets[lvl].includes(fv.value)))
128
+ if (lvl in this._selectedPaths) {
129
+ selectedAtLevel = valuesAtLevel!.filter((fv) => (this._selectedPaths[lvl].includes(fv.skuToken)))
132
130
  }
133
131
  else {
134
132
  selectedAtLevel = valuesAtLevel
135
133
  }
136
- let allSubsOfSelected: FacetValueDesc[] = []
137
- selectedAtLevel?.forEach((fvd: FacetValueDesc) => {
138
- if (fvd.sub) {
139
- allSubsOfSelected = [...allSubsOfSelected, ...fvd.sub]
134
+ let allSubsOfSelected: ProductTreeNode[] = []
135
+ selectedAtLevel?.forEach((fvd: ProductTreeNode) => {
136
+ if (fvd.subNodes) {
137
+ allSubsOfSelected = [...allSubsOfSelected, ...fvd.subNodes]
140
138
  }
141
139
  })
142
140
 
@@ -199,11 +197,9 @@ class StandaloneService
199
197
  if (!this._promo) {
200
198
  return this.cartTotal
201
199
  }
202
-
203
200
  if (!this._promo.skus) {
204
201
  return this._promoValue_unsafe(this.cartTotal)
205
202
  }
206
-
207
203
  let total = this.cartItems.reduce(
208
204
  (total, item) => {
209
205
  const itemPrice = this._promo!.skus!.includes(item.sku) ?
@@ -214,7 +210,6 @@ class StandaloneService
214
210
  },
215
211
  0
216
212
  )
217
-
218
213
  return total
219
214
  }
220
215
 
@@ -250,8 +245,8 @@ class StandaloneService
250
245
  this._currentItem = ((): ActualLineItem | undefined => {
251
246
 
252
247
  const categoriesTried: string[] = []
253
- if (this.specifiedCategories && this.specifiedCategories.length > 0) {
254
- for (let category of this.specifiedCategories) {
248
+ if (this.selectedCategories && this.selectedCategories.length > 0) {
249
+ for (let category of this.selectedCategories) {
255
250
  categoriesTried.push(category.id)
256
251
  const foundItem = category.products.find((p) => (p.sku === skuToFind))
257
252
  if (foundItem) {
@@ -281,52 +276,61 @@ class StandaloneService
281
276
  return this._currentItem
282
277
  }
283
278
 
284
- setFacets(sel: FacetsValue): Category[] {
279
+ selectPaths(sel: SelectedPaths): Category[] {
285
280
  runInAction (() => {
286
- this._selectedFacets = this._processAndValidate(sel)
281
+ this._selectedPaths = this._processAndValidate(sel)
287
282
  })
288
- return this.specifiedCategories
283
+ return this.selectedCategories
284
+ }
285
+
286
+ selectPath(skuPath: string): Category[] {
287
+ const toks = skuPath.split(SEP)
288
+ const highestLevel = toks.length - 1
289
+ const fsv: SelectedPaths = {}
290
+ for (let level = 1; level <= highestLevel; level++ ) {
291
+ fsv[level] = [toks[level]]
292
+ }
293
+ return this.selectPaths(fsv)
289
294
  }
290
295
 
291
- get facetsValue(): FacetsValue {
292
- const result: FacetsValue = {}
293
- for( let level in this._selectedFacets ) {
294
- result[level] = [...this._selectedFacets[level]]
296
+ get selectedPaths(): SelectedPaths {
297
+ const result: SelectedPaths = {}
298
+ for( let level in this._selectedPaths ) {
299
+ result[level] = [...this._selectedPaths[level]]
295
300
  }
296
301
  return result
297
302
  }
298
303
 
299
-
300
- get specifiedCategories(): Category[] {
301
- if (Object.keys(toJS(this._selectedFacets)).length === 0) {
304
+ get selectedCategories(): Category[] {
305
+ if (Object.keys(toJS(this._selectedPaths)).length === 0) {
302
306
  // FacetsDesc have never been set or unset, so cannot evaluate them
303
307
  return []
304
308
  }
305
309
 
306
- return this._rootFacet.sub!.reduce(
307
- (acc: Category[], subFacet: FacetValueDesc) => (
310
+ return this._rootNode.subNodes!.reduce(
311
+ (acc: Category[], subFacet: ProductTreeNode) => (
308
312
  // Pass the root token as a one member array
309
- this._reduceNode([this._rootFacet.value], acc, subFacet)
313
+ this._reduceNode([this._rootNode.skuToken], acc, subFacet)
310
314
  ),
311
315
  []
312
316
  )
313
317
  }
314
318
 
315
- private _reduceNode(parentPath: string[], acc: Category[], node: FacetValueDesc): Category[] {
316
- const path = [...parentPath, node.value] // Don't mutate original please :)
319
+ private _reduceNode(parentPath: string[], acc: Category[], node: ProductTreeNode): Category[] {
320
+ const path = [...parentPath, node.skuToken] // Don't mutate original please :)
317
321
  const level = path.length - 1
318
322
  // If there is no token array supplied for this level,
319
323
  // assume all are specified. Otherwise, see if the
320
324
  // current node is in the array
321
325
  const specified = (
322
- !this._selectedFacets[level]
326
+ !this._selectedPaths[level]
323
327
  ||
324
- this._selectedFacets[level].includes(node.value)
328
+ this._selectedPaths[level].includes(node.skuToken)
325
329
  )
326
330
  if (specified) {
327
331
  // Process subnodes
328
- if (node.sub && node.sub.length > 0) {
329
- return node.sub.reduce((acc, n) => (
332
+ if (node.subNodes && node.subNodes.length > 0) {
333
+ return node.subNodes.reduce((acc, n) => (
330
334
  this._reduceNode(path, acc, n)
331
335
  )
332
336
  , acc)
@@ -334,30 +338,30 @@ class StandaloneService
334
338
  // Process leaf
335
339
  const cat = this._categoryMap.get(path.join(SEP))
336
340
  if (!cat) {
337
- throw new Error("specifiedCategories WTF?!" + path.join(SEP))
341
+ throw new Error("selectedCategories WTF?!" + path.join(SEP))
338
342
  }
339
343
  acc.push(cat)
340
344
  }
341
345
  return acc
342
346
  }
343
347
 
344
- private _processAndValidate(partial: FacetsValue): FacetsValue {
348
+ private _processAndValidate(partial: SelectedPaths): SelectedPaths {
345
349
 
346
- const result: FacetsValue = {}
350
+ const result: SelectedPaths = {}
347
351
 
348
352
  let level = 1
349
- let currentSet = this._rootFacet.sub!
353
+ let currentSet = this._rootNode.subNodes!
350
354
 
351
355
  while (true) {
352
- let possibleCurrent = currentSet.map((el) => (el.value))
356
+ let possibleCurrent = currentSet.map((el) => (el.skuToken))
353
357
  const validTokens = !partial[level] ? undefined : partial[level].filter((tok) => possibleCurrent.includes(tok))
354
358
  if (!validTokens) {
355
359
  break
356
360
  }
357
361
  result[level] = validTokens
358
362
  currentSet = validTokens.map((tok) => {
359
- const fd = currentSet.find((node) => ( node.value === tok ))
360
- return (fd && fd.sub && fd.sub.length > 0) ? fd.sub : []
363
+ const fd = currentSet.find((node) => ( node.skuToken === tok ))
364
+ return (fd && fd.subNodes && fd.subNodes.length > 0) ? fd.subNodes : []
361
365
  }).flat()
362
366
  level++
363
367
  }
@@ -365,13 +369,13 @@ class StandaloneService
365
369
  return result
366
370
  }
367
371
 
368
- get specifiedItems(): LineItem[] {
369
- if (Object.keys(toJS(this._selectedFacets)).length === 0) {
372
+ get selectedItems(): LineItem[] {
373
+ if (Object.keys(toJS(this._selectedPaths)).length === 0) {
370
374
  // FacetsDesc have never been set or unset, so cannot evaluate them
371
375
  return []
372
376
  }
373
377
 
374
- return this.specifiedCategories.reduce(
378
+ return this.selectedCategories.reduce(
375
379
  (allProducts, cat) => ([...allProducts, ...(cat.products as LineItem[])]), [] as LineItem[])
376
380
  }
377
381
 
@@ -383,6 +387,14 @@ class StandaloneService
383
387
  0
384
388
  )
385
389
  }
390
+
391
+ debug_getSelectedCategories = () => {
392
+ const spec = this.selectedCategories
393
+ console.log('NUM SPEC CATS: ', spec.length)
394
+ if (spec.length > 0) {
395
+ console.log('IDS: ', (spec.map((c) => (c.id))).join(', '))
396
+ }
397
+ }
386
398
  }
387
399
 
388
400
  export {
@@ -1,5 +1,5 @@
1
1
  import type { LineItem, ObsLineItemRef } from './line-item'
2
- import type { FacetValueDesc, FacetsValue } from './facet'
2
+ import type { ProductTreeNode, SelectedPaths } from './tree-node'
3
3
  import type Category from './category'
4
4
  import type Promo from './promo'
5
5
 
@@ -29,36 +29,40 @@ interface CommerceService extends ObsLineItemRef {
29
29
 
30
30
  /**
31
31
  * Sets the tokens at each level supplied.
32
- * If a level is specifed as [], nothing will be specified.
33
- * If a level is missing (undefined), everything at that level is included
32
+ * If a level is selected as [], nothing will be selected.
33
+ * If a level is missing (undefined), everything at that level is selected
34
34
  *
35
- * This specifies one or more Category's, and all the LineItem's in them
35
+ * This selects one or more Category's, and all the LineItem's in them.
36
36
  *
37
- * An empty value object specifies all Category's and all LineItem's,
37
+ * An empty value object selects all Category's and all LineItem's,
38
38
  * */
39
- setFacets(value: FacetsValue): Category[]
40
- get facetsValue(): FacetsValue // returns a copy
41
- get specifiedItems(): LineItem[]
42
- get specifiedCategories(): Category[]
39
+ selectPaths(value: SelectedPaths): Category[]
40
+ selectPath(skuPath: string): Category[]
41
+
42
+ get selectedPaths(): SelectedPaths // returns a copy
43
+
44
+ get selectedItems(): LineItem[]
45
+ get selectedCategories(): Category[]
43
46
 
44
47
  /** Whether this path defines a Category, or if it has further levels */
45
- getFacetValuesAtSkuPath(skuPath: string): FacetValueDesc[] | undefined
48
+ getNodeAtPath(skuPath: string): ProductTreeNode | undefined
46
49
 
47
- /** Based on current value of 'level', what are the available subfacets?
48
- * If more than one value is specified at 'level' returned FacetValueDesc[]
49
- * may represent multiple sets.
50
+ /**
51
+ * Who are the subnodes selected at 'level'?
52
+ * If more than one value is selected at 'level',
53
+ * the returned ProductTreeNode[] may represent multiple sets.
50
54
  * */
51
- getFacetValuesSpecified(level: number): FacetValueDesc[] | undefined
55
+ getSelectedNodesAtLevel(level: number): ProductTreeNode[] | undefined
52
56
 
53
57
  /**
54
58
  * For convenience, so widgets can share state.
55
- * "current" is unrelated to what is "specified",
59
+ * "current" is unrelated to what is "selected",
56
60
  * ie, facets' values
57
61
  * */
58
62
  setCurrentItem(sku: string | undefined): boolean // valid sku and was set.
59
63
  /**
60
64
  * For convenience, so widgets can share state.
61
- * "current" is unrelated to what is "specified",
65
+ * "current" is unrelated to what is "selected",
62
66
  * ie, facets' values
63
67
  *
64
68
  * note: for ObsLineItemRef, there is also
package/types/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
 
2
2
  export type { default as Category } from './category'
3
3
  export type { default as CommerceService } from './commerce-service'
4
- export type { default as ItemSelector } from './item-selector'
5
4
  export type { default as Product } from './product'
6
5
  export type { default as Promo } from './promo'
7
6
 
8
7
  export * from './checkout'
8
+ export * from './item-selector'
9
9
  export * from './line-item'
10
- export * from './facet'
10
+ export * from './tree-node'
11
11
  export * from './string-mutator'
@@ -1,14 +1,21 @@
1
- import type Category from './category'
2
- import type { ObsLineItemRef } from './line-item'
1
+ import type { LineItem, ObsLineItemRef } from './line-item'
3
2
 
4
3
  interface ItemSelector {
5
- category: Category
4
+ items: LineItem[]
6
5
  selectedItemRef: ObsLineItemRef
7
6
  selectSku: (sku: string) => void
8
7
  showPrice?: boolean // true by default
9
- showQuantity?: boolean // true by default (not impl)
8
+ showQuantity?: boolean // false by default (not impl)
9
+ }
10
+
11
+ interface ItemSelectorProps extends ItemSelector {
12
+ clx?: string
13
+ soleItemClx?: string
14
+ itemClx?: string
15
+ ext?: any
10
16
  }
11
17
 
12
18
  export {
13
- type ItemSelector as default
19
+ type ItemSelector,
20
+ type ItemSelectorProps
14
21
  }
package/types/product.ts CHANGED
@@ -10,6 +10,7 @@ interface Product {
10
10
  desc?: string
11
11
  price: number
12
12
  img?: string // if undefined: (category's img exists) ? (use it) : (use generic placeholder)
13
+ imgAR?: number
13
14
  animation?: string // spline scene url
14
15
  video?: VideoDef
15
16
  }
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ interface ProductTreeNode {
4
+ skuToken: string // a token in the sku
5
+ label: string
6
+ img? : string | ReactNode // icon is required
7
+ imgAR? : number // helps with svgs
8
+
9
+ /** Short desc of what subNodes refer to.
10
+ * For example, if this node's label is 'Lux Credit',
11
+ * it's subNodesLabel would be 'Level',
12
+ * refering to membership 'levels' ('black', 'elite', etc)
13
+ */
14
+ subNodesLabel?: string
15
+ subNodes?: ProductTreeNode[]
16
+ }
17
+
18
+ // Which facets tokens are 'on' at each level
19
+ type SelectedPaths = Record<number, string[]>
20
+
21
+ export type {
22
+ ProductTreeNode,
23
+ SelectedPaths
24
+ }
package/util/index.ts CHANGED
@@ -32,19 +32,19 @@ export function formatCurrencyValue(price: number): string {
32
32
  export const getFacetValuesMutator = (level: number, cmmc: CommerceService): StringMutator => {
33
33
 
34
34
  const setLevel = (value: string, level: number ): void => {
35
- const facets = cmmc.facetsValue
35
+ const facets = cmmc.selectedPaths
36
36
  facets[level] = [value]
37
- cmmc.setFacets(facets)
38
- const subFacets = cmmc.getFacetValuesSpecified(level)
37
+ cmmc.selectPaths(facets)
38
+ const subFacets = cmmc.getSelectedNodesAtLevel(level)
39
39
  if (subFacets) {
40
- const facets = cmmc.facetsValue
41
- facets[level + 1] = [subFacets[0].value]
42
- cmmc.setFacets(facets)
40
+ const facets = cmmc.selectedPaths
41
+ facets[level + 1] = [subFacets[0].skuToken]
42
+ cmmc.selectPaths(facets)
43
43
  }
44
44
  }
45
45
 
46
46
  const getLevelValueSafe = (level: number): string | null => {
47
- const facets = cmmc.facetsValue
47
+ const facets = cmmc.selectedPaths
48
48
  if (!(level in facets) || facets[level].length === 0 ) {
49
49
  return null
50
50
  }
@@ -52,8 +52,8 @@ export const getFacetValuesMutator = (level: number, cmmc: CommerceService): Str
52
52
  }
53
53
 
54
54
  return {
55
- get: () => (getLevelValueSafe(level)),
56
- set: (v: string) => {setLevel(v, level)}
55
+ get: (): string | null => (getLevelValueSafe(level)),
56
+ set: (v: string): void => {setLevel(v, level)}
57
57
  } satisfies StringMutator
58
58
  }
59
59
 
@@ -9,7 +9,7 @@ import {
9
9
  } from 'next-usequerystate'
10
10
 
11
11
  import { useCommerce } from '../service/context'
12
- import type { FacetsValue } from '../types'
12
+ import type { SelectedPaths } from '../types'
13
13
 
14
14
  const PLEASE_SELECT_FACETS = 'Please select an option from each group.'
15
15
 
@@ -32,7 +32,7 @@ const useSyncSkuParamWithCurrentItem = (
32
32
  useEffect(() => {
33
33
 
34
34
  return reaction(() => ({
35
- specifiedCat: cmmc.specifiedCategories.length === 1 ? cmmc.specifiedCategories[0] : undefined,
35
+ specifiedCat: cmmc.selectedCategories.length === 1 ? cmmc.selectedCategories[0] : undefined,
36
36
  currentItem: cmmc.currentItem
37
37
  }),
38
38
  ({specifiedCat, currentItem}) => {
@@ -55,14 +55,14 @@ const useSyncSkuParamWithCurrentItem = (
55
55
 
56
56
  const setCurrentCategoryFromSku = (sku: string) => {
57
57
  const toks: string[] = sku.split('-')
58
- const fv: FacetsValue = {}
59
- // TODO: confirm that extra trailing nonsense tokens won't break setFacets()
58
+ const fv: SelectedPaths = {}
59
+ // TODO: confirm that extra trailing nonsense tokens won't break selectPaths()
60
60
  for (let i = 1; i < categoryLevel; i++) {
61
61
  if (i in toks) {
62
62
  fv[i] = [toks[i]]
63
63
  }
64
64
  }
65
- cmmc.setFacets(fv)
65
+ cmmc.selectPaths(fv)
66
66
  }
67
67
 
68
68
  // setCI returns true if it's a recognized sku
@@ -1,27 +0,0 @@
1
- 'use client'
2
- import React, {type PropsWithChildren} from 'react'
3
-
4
- import BuyItemPopup from './buy-item-popup'
5
- import BuyItemMobileDrawer from './buy-item-mobile-drawer'
6
-
7
- const BuyItemButtonWrapper: React.FC<{
8
- skuPath: string
9
- desktopTrigger: React.ReactNode
10
- mobileTrigger: React.ReactNode
11
- popupClx?: string
12
- }> = ({
13
- skuPath,
14
- desktopTrigger,
15
- mobileTrigger,
16
- popupClx=''
17
- }) => (<>
18
- <BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
19
- {desktopTrigger}
20
- </BuyItemPopup>
21
- <BuyItemMobileDrawer skuPath={skuPath} trigger={mobileTrigger} />
22
- </>
23
- )
24
-
25
-
26
-
27
- export default BuyItemButtonWrapper
@@ -1,34 +0,0 @@
1
- 'use client'
2
- import React, {type PropsWithChildren} from 'react'
3
-
4
- import { type ButtonVariants, type ButtonSizes, Button } from '@hanzo/ui/primitives'
5
-
6
- import BuyItemPopup from './buy-item-popup'
7
- import BuyItemMobileDrawer from './buy-item-mobile-drawer'
8
- import { cn } from '@hanzo/ui/util'
9
-
10
- const BuyItemButton: React.FC<PropsWithChildren & {
11
- skuPath: string
12
- variant? : ButtonVariants
13
- size?: ButtonSizes
14
- /* rounded?: ButtonRounded // wait for version bump*/
15
- className?: string
16
- popupClx?: string
17
- }> = ({
18
- skuPath,
19
- variant,
20
- size,
21
- children,
22
- className='',
23
- popupClx=''
24
- }) => (<>
25
- <BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
26
- <Button size={size} variant={variant} className={cn(className, 'hidden md:flex')}>{children}</Button>
27
- </BuyItemPopup>
28
- <BuyItemMobileDrawer skuPath={skuPath} trigger={<Button size={size} variant={variant} className={cn(className, 'md:hidden')}>{children}</Button>} />
29
- </>
30
- )
31
-
32
-
33
-
34
- export default BuyItemButton
@@ -1,92 +0,0 @@
1
- 'use client'
2
- import React, { useRef, useEffect } from 'react'
3
- import { autorun } from 'mobx'
4
- import { observer } from 'mobx-react-lite'
5
-
6
- import { cn } from '@hanzo/ui/util'
7
-
8
- import type { FacetsValue } from '../../types'
9
- import { useCommerce } from '../../service/context'
10
- import { getFacetValuesMutator } from '../../util'
11
- import FacetValuesWidget from '../facet-values-widget'
12
- import SelectCategoryItemCard from './select-category-item-card'
13
-
14
-
15
- const BuyItemCard: React.FC<{
16
- skuPath: string
17
- mobile?: boolean
18
- className?: string
19
- onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
20
- }> = observer(({
21
- skuPath,
22
- mobile=false,
23
- className='',
24
- onQuantityChanged
25
- }) => {
26
-
27
- const cmmc = useCommerce()
28
- const levelRef = useRef<number>(-1)
29
-
30
- const cat = cmmc.getCategory(skuPath)
31
- const facets = cat ? undefined : cmmc.getFacetValuesAtSkuPath(skuPath)
32
-
33
- useEffect(() => {
34
-
35
- const toks = skuPath.split('-')
36
- levelRef.current = toks.length - 1
37
- const fsv: FacetsValue = {}
38
- for (let level = 1; level <= levelRef.current; level++ ) {
39
- fsv[level] = [toks[level]]
40
- }
41
- if (facets) {
42
- fsv[levelRef.current + 1] = [facets[0].value]
43
- }
44
- cmmc.setFacets(fsv)
45
-
46
- return autorun(() => {
47
- const cats = cmmc.specifiedCategories
48
- // Original cat was legit
49
- if (cat && (cats.length === 0 || cats[0].id !== cat.id)) {
50
- if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cat.id) {
51
- cmmc.setCurrentItem(cat.products[0].sku)
52
- }
53
- }
54
- else if (cats.length > 0) {
55
- if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cats[0].id) {
56
- cmmc.setCurrentItem(cats[0].products[0].sku)
57
- }
58
- }
59
- })
60
- }, [cat, facets])
61
-
62
- const renderFacetTabs = facets && levelRef.current > 0
63
-
64
- return (
65
- <div className={className} >
66
- {renderFacetTabs && (
67
- <FacetValuesWidget
68
- className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b-2 border-level-3 mb-2 -mr-2 -ml-2')}
69
- isMobile={false}
70
- mutator={getFacetValuesMutator(levelRef.current + 1, cmmc)}
71
- itemClx='flex-col h-auto gap-0 pb-1 pt-3 px-3'
72
- buttonClx={'h-full !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg ' +
73
- '!border-r !border-t !border-level-3'}
74
- facetValues={facets}
75
- />
76
- )}
77
- {cmmc.specifiedCategories[0] && (
78
- <SelectCategoryItemCard
79
- noTitle
80
- mobile={mobile}
81
- category={cmmc.specifiedCategories[0]}
82
- selectedItemRef={cmmc /* ...conveniently. :) */ }
83
- selectSku={cmmc.setCurrentItem.bind(cmmc)}
84
- className=''
85
- onQuantityChanged={onQuantityChanged}
86
- />
87
- )}
88
- </div >
89
- )
90
- })
91
-
92
- export default BuyItemCard