@hanzo/commerce 7.1.5 → 7.1.7

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 (84) hide show
  1. package/components/Icons.tsx +34 -34
  2. package/components/add-to-cart-widget.tsx +181 -181
  3. package/components/buy/buy-card.tsx +259 -259
  4. package/components/buy/carousel-buy-card.tsx +245 -245
  5. package/components/buy/multi-family/all-variants-carousel.tsx +261 -261
  6. package/components/buy/multi-family/family-carousel/index.tsx +77 -77
  7. package/components/buy/multi-family/family-carousel/slide.tsx +83 -83
  8. package/components/buy/multi-family/family-carousel/state.ts +87 -87
  9. package/components/buy/multi-family/index.ts +2 -2
  10. package/components/buy/single-family-selector.tsx +90 -90
  11. package/components/buy/title-and-byline.tsx +25 -25
  12. package/components/cart/cart-accordian.tsx +59 -59
  13. package/components/cart/cart-panel/cart-line-item.tsx +76 -76
  14. package/components/cart/cart-panel/index.tsx +161 -159
  15. package/components/cart/cart-panel/promo-code.tsx +98 -98
  16. package/components/checkout/payment-step-form/card-icon-row.tsx +26 -26
  17. package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -32
  18. package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -13
  19. package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -25
  20. package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -26
  21. package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -27
  22. package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -25
  23. package/components/checkout/payment-step-form/cc-button.tsx +17 -17
  24. package/components/checkout/payment-step-form/contact-form.tsx +49 -49
  25. package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -11
  26. package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -20
  27. package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -13
  28. package/components/checkout/payment-step-form/index.tsx +122 -122
  29. package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -79
  30. package/components/checkout/payment-step-form/methods/card.tsx +232 -232
  31. package/components/checkout/payment-step-form/methods/crypto.tsx +227 -227
  32. package/components/checkout/payment-step-form/methods/index.ts +22 -22
  33. package/components/checkout/shipping-step-form.tsx +172 -172
  34. package/components/index.ts +12 -12
  35. package/components/item/product-card.tsx +48 -48
  36. package/components/item-selector/button.tsx +188 -188
  37. package/components/item-selector/carousel/index.tsx +197 -197
  38. package/components/item-selector/carousel/slider.tsx +40 -40
  39. package/components/item-selector/index.ts +5 -5
  40. package/components/item-selector/quantity-indicator.tsx +48 -48
  41. package/components/node-tabs/index.tsx +91 -91
  42. package/components/node-tabs/node-image.tsx +31 -31
  43. package/context/index.tsx +45 -45
  44. package/index.ts +12 -12
  45. package/package.json +60 -60
  46. package/service/debug.ts +41 -41
  47. package/service/get-instance.ts +2 -2
  48. package/service/impls/standalone/actual-line-item.ts +121 -121
  49. package/service/impls/standalone/index.ts +62 -62
  50. package/service/impls/standalone/localStorage.ts +34 -34
  51. package/service/impls/standalone/orders/firebase-app.ts +14 -14
  52. package/service/impls/standalone/orders/index.ts +128 -128
  53. package/service/impls/standalone/standalone-service.ts +550 -550
  54. package/service/path-utils.ts +25 -25
  55. package/service/sep.ts +6 -6
  56. package/tsconfig.json +10 -10
  57. package/types/README.md +1 -1
  58. package/types/category-node.ts +49 -49
  59. package/types/checkout.ts +46 -46
  60. package/types/commerce-config.ts +13 -13
  61. package/types/commerce-service.ts +121 -121
  62. package/types/family.ts +25 -25
  63. package/types/index.ts +15 -15
  64. package/types/item-selector.ts +96 -96
  65. package/types/line-item.ts +29 -29
  66. package/types/multi-family-selector-props.ts +19 -19
  67. package/types/product.ts +20 -20
  68. package/types/promo.ts +9 -9
  69. package/types/selection-ui-specifier.ts +51 -51
  70. package/types/string-mutator.ts +13 -13
  71. package/types/token-separators.ts +7 -7
  72. package/util/analytics.ts +20 -20
  73. package/util/countries.ts +195 -195
  74. package/util/error.ts +34 -34
  75. package/util/index.ts +70 -70
  76. package/util/item-selector-options-accessor.ts +34 -34
  77. package/util/line-item-ref.ts +23 -23
  78. package/util/multi-family-selector-options-accessor.ts +14 -14
  79. package/util/obs-string-mutator.ts +22 -22
  80. package/util/product-media-accessor.ts +58 -58
  81. package/util/promo-codes.ts +108 -108
  82. package/util/selection-ui-specifiers.ts +29 -29
  83. package/util/square-payment.ts +42 -42
  84. package/util/use-sync-sku-param-w-current-item.ts +88 -88
@@ -1,245 +1,245 @@
1
- 'use client'
2
- import React, {
3
- useRef,
4
- useEffect,
5
- type ComponentType,
6
- useState
7
- } from 'react'
8
- import { observer } from 'mobx-react-lite'
9
-
10
- import { cn } from '@hanzo/ui/util'
11
-
12
- import type {
13
- ItemSelectorProps,
14
- LineItem,
15
- Family,
16
- CategoryNode,
17
- CategoryNodeRole,
18
- SelectionUISpecifier,
19
- ItemSelectorOptions,
20
- MultiFamilySelectorProps,
21
- MultiFamilySelectorOptions,
22
- } from '../../types'
23
-
24
- import { useCommerce } from '../../context'
25
- import { getSelectionUISpecifier } from '../../util'
26
-
27
- import { CarouselItemSelector, ButtonItemSelector } from '../item-selector'
28
- import SingleFamilySelector from './single-family-selector'
29
- import { FamilyCarousel, AllVariantsCarousel } from './multi-family'
30
-
31
- import AddToCartWidget from '../add-to-cart-widget'
32
-
33
- const SCROLL = {
34
- scrollAfter: 5,
35
- scrollHeightClx: 'h-[65vh]'
36
- }
37
-
38
- const MEDIA_CONSTRAINT = {w: 200, h: 200}
39
-
40
- const sortItems = (items: LineItem[], sort: 'asc' | 'desc' | 'none'): LineItem[] => (
41
- ((sort === 'asc') ?
42
- items.sort((a: LineItem, b: LineItem): number => (a.price - b.price))
43
- :
44
- ((sort === 'desc') ?
45
- items.sort((a: LineItem, b: LineItem): number => (b.price - a.price ))
46
- :
47
- items
48
- )
49
- )
50
- )
51
-
52
- const CarouselBuyCard: React.FC<{
53
- skuPath: string
54
- checkoutButton: React.ReactNode
55
- clx?: string
56
- selectorClx?: string
57
- addBtnClx?: string
58
- buttonsAreaClx?: string
59
- mobile?: boolean
60
- onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
61
- }> = ({
62
- skuPath,
63
- checkoutButton,
64
- clx='',
65
- selectorClx='',
66
- addBtnClx='',
67
- buttonsAreaClx='',
68
- mobile=false,
69
- onQuantityChanged,
70
- }) => {
71
-
72
- const cmmc = useCommerce()
73
-
74
- const r = useRef<{
75
- role: CategoryNodeRole
76
- item: LineItem | undefined
77
- family: Family | undefined
78
- families: Family[] | undefined
79
- node: CategoryNode
80
- uiSpec: SelectionUISpecifier
81
- single?: {
82
- items: LineItem[]
83
- Selector: ComponentType<ItemSelectorProps>
84
- selOptions: ItemSelectorOptions | undefined
85
- scrollable: boolean
86
- showItemMedia: boolean
87
- }
88
- multi?: {
89
- Selector: ComponentType<MultiFamilySelectorProps>
90
- selectorOptions: MultiFamilySelectorOptions | undefined
91
- itemOptions: ItemSelectorOptions | undefined
92
- initialFamilyId: string
93
- }
94
- } | undefined>(undefined)
95
-
96
- const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
97
-
98
- useEffect(() => {
99
-
100
- if (!skuPath || skuPath.length === 0 ) {
101
- // The component is being hidden (w an amination)
102
- // keep things the same so no layout jump
103
- return
104
- }
105
-
106
- const peek = cmmc.peek(skuPath)
107
- if (typeof peek === 'string') {
108
- throw new Error(peek)
109
- }
110
- if (peek.role === 'non-outermost') {
111
- throw new Error(`BuyCard: skuPath ${skuPath} isn't an outermost tree node or product family!`)
112
- }
113
-
114
- const uiSpec = getSelectionUISpecifier(skuPath)
115
-
116
- r.current = {
117
- ...peek,
118
- node: peek.node!, // else exception was thrown.
119
- uiSpec,
120
- }
121
-
122
- if (peek.role === 'single-family') {
123
-
124
- const sort = (): 'none' | 'asc' | 'desc' => {
125
- if (!uiSpec.singleFamily?.options) {
126
- return 'none'
127
- }
128
- const options = uiSpec.singleFamily.options
129
- const showSlider = 'showSlider' in options ? options.showSlider! : true
130
- return ('sort' in options) ?
131
- options.sort!
132
- :
133
- (showSlider ? 'asc' : 'none')
134
- }
135
-
136
- const items = peek.family!.products as LineItem[]
137
- const Selector: ComponentType<ItemSelectorProps> =
138
- (uiSpec.singleFamily?.type === 'buttons') ? ButtonItemSelector : CarouselItemSelector
139
- const selOptions = uiSpec.singleFamily?.options
140
-
141
- r.current.single = {
142
- items: sortItems(items, sort()),
143
- Selector,
144
- selOptions,
145
- scrollable: !!(items.length > SCROLL.scrollAfter),
146
- showItemMedia: uiSpec.singleFamily?.type !== 'carousel'
147
- }
148
-
149
- const currItem = peek.item ?? r.current.single.items[0]
150
- cmmc.setCurrentItem(currItem.sku)
151
- }
152
- else {
153
- const initialFamily = peek.family ? peek.family : peek.families![0]
154
- // TODO: Does this ever need to be sorted??
155
- const currItem = peek.item ?? initialFamily.products[0]
156
-
157
- // sets currentFamily as well
158
- cmmc.setCurrentItem(currItem.sku)
159
-
160
- r.current.multi = {
161
- Selector: (uiSpec.multiFamily?.type === 'family-carousel') ?
162
- FamilyCarousel
163
- :
164
- AllVariantsCarousel,
165
- itemOptions: uiSpec.multiFamily?.itemOptions,
166
- selectorOptions: uiSpec.multiFamily?.selectorOptions,
167
- initialFamilyId: initialFamily.id
168
- }
169
- }
170
-
171
- // Must do this since Dialog code takes this comp out of the React shadow DOM
172
- // (only in prod apparently.)
173
- setChangeMeToRerender(!changeMeToRerender)
174
- }, [skuPath])
175
-
176
- const MultiFamilyUI: React.FC<{
177
- Selector: ComponentType<MultiFamilySelectorProps>
178
- selectorOptions: MultiFamilySelectorOptions | undefined
179
- itemOptions: ItemSelectorOptions | undefined
180
- families: Family[]
181
- parent: CategoryNode
182
- clx?: string
183
- }> = ({
184
- Selector,
185
- itemOptions,
186
- selectorOptions,
187
- families,
188
- parent,
189
- clx=''
190
- }) => (
191
- <Selector
192
- families={families}
193
- parent={parent}
194
- clx={clx}
195
- itemOptions={itemOptions}
196
- selectorOptions={selectorOptions}
197
- mediaConstraint={MEDIA_CONSTRAINT}
198
- mobile={mobile}
199
- />
200
- )
201
-
202
- const Buttons: React.FC<{clx?: string}> = observer(({
203
- clx=''
204
- }) => (cmmc.currentItem ? (
205
- <div className={clx}>
206
- <AddToCartWidget
207
- item={cmmc.currentItem}
208
- onQuantityChanged={onQuantityChanged}
209
- variant={cmmc.cartEmpty ? 'primary' : 'outline'}
210
- className={addBtnClx}
211
- />
212
- {!cmmc.cartEmpty && checkoutButton}
213
- </div>
214
- ) : null))
215
-
216
- return (
217
- <div className={cn(
218
- 'px-4 md:px-6 pt-3 pb-4 flex flex-col gap-1 items-center',
219
- r.current?.single?.scrollable ? SCROLL.scrollHeightClx : 'h-auto',
220
- clx,
221
- )}>
222
- {r.current?.single ? (
223
- <SingleFamilySelector
224
- {...r.current.single}
225
- mediaConstraint={MEDIA_CONSTRAINT}
226
- mobile={mobile}
227
- clx={selectorClx}
228
- />
229
- ) : (r.current?.multi && r.current.families && /* safegaurd for first render, etc. */ (
230
- <MultiFamilyUI
231
- {...r.current.multi}
232
- families={r.current.families}
233
- parent={r.current.node}
234
- clx={selectorClx}
235
- />
236
- ))}
237
- <Buttons clx={cn(
238
- 'self-stretch mt-8 flex flex-col items-center gap-3 shrink-0 grow-0',
239
- buttonsAreaClx
240
- )}/>
241
- </div >
242
- )
243
- }
244
-
245
- export default CarouselBuyCard
1
+ 'use client'
2
+ import React, {
3
+ useRef,
4
+ useEffect,
5
+ type ComponentType,
6
+ useState
7
+ } from 'react'
8
+ import { observer } from 'mobx-react-lite'
9
+
10
+ import { cn } from '@hanzo/ui/util'
11
+
12
+ import type {
13
+ ItemSelectorProps,
14
+ LineItem,
15
+ Family,
16
+ CategoryNode,
17
+ CategoryNodeRole,
18
+ SelectionUISpecifier,
19
+ ItemSelectorOptions,
20
+ MultiFamilySelectorProps,
21
+ MultiFamilySelectorOptions,
22
+ } from '../../types'
23
+
24
+ import { useCommerce } from '../../context'
25
+ import { getSelectionUISpecifier } from '../../util'
26
+
27
+ import { CarouselItemSelector, ButtonItemSelector } from '../item-selector'
28
+ import SingleFamilySelector from './single-family-selector'
29
+ import { FamilyCarousel, AllVariantsCarousel } from './multi-family'
30
+
31
+ import AddToCartWidget from '../add-to-cart-widget'
32
+
33
+ const SCROLL = {
34
+ scrollAfter: 5,
35
+ scrollHeightClx: 'h-[65vh]'
36
+ }
37
+
38
+ const MEDIA_CONSTRAINT = {w: 200, h: 200}
39
+
40
+ const sortItems = (items: LineItem[], sort: 'asc' | 'desc' | 'none'): LineItem[] => (
41
+ ((sort === 'asc') ?
42
+ items.sort((a: LineItem, b: LineItem): number => (a.price - b.price))
43
+ :
44
+ ((sort === 'desc') ?
45
+ items.sort((a: LineItem, b: LineItem): number => (b.price - a.price ))
46
+ :
47
+ items
48
+ )
49
+ )
50
+ )
51
+
52
+ const CarouselBuyCard: React.FC<{
53
+ skuPath: string
54
+ checkoutButton: React.ReactNode
55
+ clx?: string
56
+ selectorClx?: string
57
+ addBtnClx?: string
58
+ buttonsAreaClx?: string
59
+ mobile?: boolean
60
+ onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
61
+ }> = ({
62
+ skuPath,
63
+ checkoutButton,
64
+ clx='',
65
+ selectorClx='',
66
+ addBtnClx='',
67
+ buttonsAreaClx='',
68
+ mobile=false,
69
+ onQuantityChanged,
70
+ }) => {
71
+
72
+ const cmmc = useCommerce()
73
+
74
+ const r = useRef<{
75
+ role: CategoryNodeRole
76
+ item: LineItem | undefined
77
+ family: Family | undefined
78
+ families: Family[] | undefined
79
+ node: CategoryNode
80
+ uiSpec: SelectionUISpecifier
81
+ single?: {
82
+ items: LineItem[]
83
+ Selector: ComponentType<ItemSelectorProps>
84
+ selOptions: ItemSelectorOptions | undefined
85
+ scrollable: boolean
86
+ showItemMedia: boolean
87
+ }
88
+ multi?: {
89
+ Selector: ComponentType<MultiFamilySelectorProps>
90
+ selectorOptions: MultiFamilySelectorOptions | undefined
91
+ itemOptions: ItemSelectorOptions | undefined
92
+ initialFamilyId: string
93
+ }
94
+ } | undefined>(undefined)
95
+
96
+ const [changeMeToRerender, setChangeMeToRerender] = useState<boolean>(false)
97
+
98
+ useEffect(() => {
99
+
100
+ if (!skuPath || skuPath.length === 0 ) {
101
+ // The component is being hidden (w an amination)
102
+ // keep things the same so no layout jump
103
+ return
104
+ }
105
+
106
+ const peek = cmmc.peek(skuPath)
107
+ if (typeof peek === 'string') {
108
+ throw new Error(peek)
109
+ }
110
+ if (peek.role === 'non-outermost') {
111
+ throw new Error(`BuyCard: skuPath ${skuPath} isn't an outermost tree node or product family!`)
112
+ }
113
+
114
+ const uiSpec = getSelectionUISpecifier(skuPath)
115
+
116
+ r.current = {
117
+ ...peek,
118
+ node: peek.node!, // else exception was thrown.
119
+ uiSpec,
120
+ }
121
+
122
+ if (peek.role === 'single-family') {
123
+
124
+ const sort = (): 'none' | 'asc' | 'desc' => {
125
+ if (!uiSpec.singleFamily?.options) {
126
+ return 'none'
127
+ }
128
+ const options = uiSpec.singleFamily.options
129
+ const showSlider = 'showSlider' in options ? options.showSlider! : true
130
+ return ('sort' in options) ?
131
+ options.sort!
132
+ :
133
+ (showSlider ? 'asc' : 'none')
134
+ }
135
+
136
+ const items = peek.family!.products as LineItem[]
137
+ const Selector: ComponentType<ItemSelectorProps> =
138
+ (uiSpec.singleFamily?.type === 'buttons') ? ButtonItemSelector : CarouselItemSelector
139
+ const selOptions = uiSpec.singleFamily?.options
140
+
141
+ r.current.single = {
142
+ items: sortItems(items, sort()),
143
+ Selector,
144
+ selOptions,
145
+ scrollable: !!(items.length > SCROLL.scrollAfter),
146
+ showItemMedia: uiSpec.singleFamily?.type !== 'carousel'
147
+ }
148
+
149
+ const currItem = peek.item ?? r.current.single.items[0]
150
+ cmmc.setCurrentItem(currItem.sku)
151
+ }
152
+ else {
153
+ const initialFamily = peek.family ? peek.family : peek.families![0]
154
+ // TODO: Does this ever need to be sorted??
155
+ const currItem = peek.item ?? initialFamily.products[0]
156
+
157
+ // sets currentFamily as well
158
+ cmmc.setCurrentItem(currItem.sku)
159
+
160
+ r.current.multi = {
161
+ Selector: (uiSpec.multiFamily?.type === 'family-carousel') ?
162
+ FamilyCarousel
163
+ :
164
+ AllVariantsCarousel,
165
+ itemOptions: uiSpec.multiFamily?.itemOptions,
166
+ selectorOptions: uiSpec.multiFamily?.selectorOptions,
167
+ initialFamilyId: initialFamily.id
168
+ }
169
+ }
170
+
171
+ // Must do this since Dialog code takes this comp out of the React shadow DOM
172
+ // (only in prod apparently.)
173
+ setChangeMeToRerender(!changeMeToRerender)
174
+ }, [skuPath])
175
+
176
+ const MultiFamilyUI: React.FC<{
177
+ Selector: ComponentType<MultiFamilySelectorProps>
178
+ selectorOptions: MultiFamilySelectorOptions | undefined
179
+ itemOptions: ItemSelectorOptions | undefined
180
+ families: Family[]
181
+ parent: CategoryNode
182
+ clx?: string
183
+ }> = ({
184
+ Selector,
185
+ itemOptions,
186
+ selectorOptions,
187
+ families,
188
+ parent,
189
+ clx=''
190
+ }) => (
191
+ <Selector
192
+ families={families}
193
+ parent={parent}
194
+ clx={clx}
195
+ itemOptions={itemOptions}
196
+ selectorOptions={selectorOptions}
197
+ mediaConstraint={MEDIA_CONSTRAINT}
198
+ mobile={mobile}
199
+ />
200
+ )
201
+
202
+ const Buttons: React.FC<{clx?: string}> = observer(({
203
+ clx=''
204
+ }) => (cmmc.currentItem ? (
205
+ <div className={clx}>
206
+ <AddToCartWidget
207
+ item={cmmc.currentItem}
208
+ onQuantityChanged={onQuantityChanged}
209
+ variant={cmmc.cartEmpty ? 'primary' : 'outline'}
210
+ className={addBtnClx}
211
+ />
212
+ {!cmmc.cartEmpty && checkoutButton}
213
+ </div>
214
+ ) : null))
215
+
216
+ return (
217
+ <div className={cn(
218
+ 'px-4 md:px-6 pt-3 pb-4 flex flex-col gap-1 items-center',
219
+ r.current?.single?.scrollable ? SCROLL.scrollHeightClx : 'h-auto',
220
+ clx,
221
+ )}>
222
+ {r.current?.single ? (
223
+ <SingleFamilySelector
224
+ {...r.current.single}
225
+ mediaConstraint={MEDIA_CONSTRAINT}
226
+ mobile={mobile}
227
+ clx={selectorClx}
228
+ />
229
+ ) : (r.current?.multi && r.current.families && /* safegaurd for first render, etc. */ (
230
+ <MultiFamilyUI
231
+ {...r.current.multi}
232
+ families={r.current.families}
233
+ parent={r.current.node}
234
+ clx={selectorClx}
235
+ />
236
+ ))}
237
+ <Buttons clx={cn(
238
+ 'self-stretch mt-8 flex flex-col items-center gap-3 shrink-0 grow-0',
239
+ buttonsAreaClx
240
+ )}/>
241
+ </div >
242
+ )
243
+ }
244
+
245
+ export default CarouselBuyCard