@hanzo/commerce 7.3.1 → 7.3.2

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 (83) hide show
  1. package/components/Icons.tsx +34 -34
  2. package/components/add-to-cart-widget.tsx +183 -183
  3. package/components/buy/buy-card.tsx +259 -259
  4. package/components/buy/carousel-buy-card.tsx +242 -242
  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-panel/cart-line-item.tsx +76 -76
  13. package/components/cart/cart-panel/index.tsx +154 -154
  14. package/components/cart/cart-panel/promo-code.tsx +106 -106
  15. package/components/cart/cart-panel/total-area.tsx +60 -60
  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 +11 -11
  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/index.ts +12 -12
  44. package/package.json +60 -60
  45. package/service/context.tsx +45 -45
  46. package/service/debug.ts +41 -41
  47. package/service/impls/standalone/actual-line-item.ts +135 -135
  48. package/service/impls/standalone/get-instance.ts +64 -64
  49. package/service/impls/standalone/index.ts +579 -579
  50. package/service/impls/standalone/order/firebase.ts +14 -14
  51. package/service/impls/standalone/order/index.ts +128 -128
  52. package/service/impls/standalone/persistence.ts +33 -33
  53. package/service/path-utils.ts +25 -25
  54. package/service/sep.ts +6 -6
  55. package/tsconfig.json +10 -10
  56. package/types/README.md +1 -1
  57. package/types/category-node.ts +49 -49
  58. package/types/checkout.ts +46 -46
  59. package/types/commerce-config.ts +13 -13
  60. package/types/commerce-service.ts +127 -127
  61. package/types/family.ts +25 -25
  62. package/types/index.ts +15 -15
  63. package/types/item-selector.ts +96 -96
  64. package/types/line-item.ts +29 -29
  65. package/types/multi-family-selector-props.ts +19 -19
  66. package/types/product.ts +20 -20
  67. package/types/promo.ts +9 -9
  68. package/types/selection-ui-specifier.ts +51 -51
  69. package/types/string-mutator.ts +13 -13
  70. package/types/token-separators.ts +7 -7
  71. package/util/analytics.ts +20 -20
  72. package/util/countries.ts +195 -195
  73. package/util/error.ts +34 -34
  74. package/util/index.ts +70 -70
  75. package/util/item-selector-options-accessor.ts +34 -34
  76. package/util/line-item-ref.ts +23 -23
  77. package/util/multi-family-selector-options-accessor.ts +14 -14
  78. package/util/obs-string-mutator.ts +22 -22
  79. package/util/product-media-accessor.ts +58 -58
  80. package/util/promo-codes.ts +108 -108
  81. package/util/selection-ui-specifiers.ts +29 -29
  82. package/util/square-payment.ts +42 -42
  83. package/util/use-sync-sku-param-w-current-item.ts +88 -88
@@ -1,227 +1,227 @@
1
- 'use client'
2
-
3
- import React, { useEffect, useState } from 'react'
4
- import { autorun } from 'mobx'
5
- import { observer } from 'mobx-react-lite'
6
-
7
- import { ethers } from 'ethers'
8
-
9
- import {
10
- Button,
11
- Input,
12
- Select,
13
- SelectContent,
14
- SelectGroup,
15
- SelectItem,
16
- SelectTrigger,
17
- SelectValue,
18
- toast
19
- } from '@hanzo/ui/primitives'
20
-
21
- import Eth from '../crypto-icons/eth'
22
- import { useCommerce } from '../../../../service/context'
23
- import type { PaymentMethodComponentProps } from '../../../../types'
24
- import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
25
-
26
- import ContactForm from '../contact-form'
27
-
28
- declare global {
29
- interface Window{
30
- ethereum?: any
31
- }
32
- }
33
-
34
- const PayWithCrypto: React.FC<PaymentMethodComponentProps> = observer(({
35
- onDone,
36
- transactionStatus,
37
- setTransactionStatus,
38
- storePaymentInfo,
39
- contactForm
40
- }) => {
41
- const cmmc = useCommerce()
42
-
43
- const [loadingPrice, setLoadingPrice] = useState(false)
44
- //const [selectedToken, setSelectedToken] = useState('eth')
45
- const [amount, setAmount] = useState<number>()
46
- const [provider, setProvider] = useState<ethers.BrowserProvider>()
47
-
48
- //const selectedToken = 'eth'
49
-
50
- useEffect(() => {
51
- setTransactionStatus('unpaid')
52
- // responding to changes in user.walletAddress
53
- return autorun(() => {
54
- const newProvider = new ethers.BrowserProvider(window.ethereum)
55
- setProvider(newProvider)
56
- })
57
- }, [])
58
-
59
- // Get latest USD -> ETH exchange rate
60
- useEffect(() => {
61
- const fetchPrice = () => {
62
- setLoadingPrice(true)
63
- fetch(process.env.NEXT_PUBLIC_ETH_EXCHANGE_RATE_API ?? '')
64
- .then(res => res.json())
65
- .then((exchangeRate) => {
66
- const oneUsdInWei = (10**18) / exchangeRate.data.amount
67
- const usdAmountInWei = oneUsdInWei * cmmc.promoAppliedCartTotal
68
- setAmount(usdAmountInWei)
69
- setLoadingPrice(false)
70
- })
71
- }
72
-
73
- // Call immediately on load
74
- fetchPrice()
75
-
76
- // Then set interval to call every 30 seconds
77
- const interval = setInterval(fetchPrice, 30000)
78
-
79
- return () => clearInterval(interval)
80
- }, [cmmc.promoAppliedCartTotal])
81
-
82
- const sendPayment = async (ether: number) => {
83
- contactForm.handleSubmit(async () => {
84
- // Check that we are on ethereum network
85
- try {
86
- await window.ethereum.request({
87
- method: 'wallet_switchEthereumChain',
88
- params: [{ chainId: "0x1"}],
89
- })
90
- const newProvider = new ethers.BrowserProvider(window.ethereum)
91
- setProvider(newProvider)
92
- } catch (err) {
93
- toast('Please switch your wallet to the Ethereum network.')
94
- return
95
- }
96
-
97
- try {
98
- if (!provider) {
99
- // :aa TODO string table
100
- throw new Error('No crypto wallet found. Please install it.')
101
- }
102
-
103
- await window.ethereum.send('eth_requestAccounts')
104
-
105
- const signer = await provider.getSigner()
106
- ethers.getAddress(process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS ?? '')
107
- const price = ethers.parseEther(ether.toString())
108
- const tx = await signer.sendTransaction({
109
- to: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
110
- value: price
111
- })
112
- console.log({ ether, addr: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS })
113
- console.log('tx', tx)
114
- setTransactionStatus('paid')
115
- await storePaymentInfo({
116
- ether,
117
- transactionHash: tx.hash,
118
- to: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
119
- paymentMethod: 'crypto'
120
- })
121
-
122
- provider.waitForTransaction(tx.hash)
123
- .then(async (receipt) => {
124
- console.log(receipt)
125
- await storePaymentInfo({
126
- ether,
127
- addr: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
128
- receipt,
129
- paymentMethod: 'crypto'
130
- })
131
- sendGAEvent('purchase', {
132
- transaction_id: tx.hash,
133
- value: price,
134
- currency: 'ETH',
135
- items: cmmc.cartItems.map((item) => ({
136
- item_id: item.sku,
137
- item_name: item.title,
138
- item_category: item.familyId,
139
- price: item.price,
140
- quantity: item.quantity
141
- })),
142
- })
143
- sendFBEvent('Purchase', {
144
- content_ids: cmmc.cartItems.map((item) => item.sku),
145
- contents: cmmc.cartItems.map(item => ({
146
- id: item.sku,
147
- quantity: item.quantity
148
- })),
149
- num_items: cmmc.cartItems.length,
150
- value: price,
151
- currency: 'ETH',
152
- })
153
- setTransactionStatus('confirmed')
154
- })
155
- .catch((error) => {
156
- console.error(error)
157
- setTransactionStatus('error')
158
- })
159
- } catch (err) {
160
- console.log(err)
161
- // :aa TODO string table
162
- toast('Not enough funds in your wallet')
163
- }
164
- })()
165
- }
166
-
167
- const nextStep = async () => {
168
- await storePaymentInfo({
169
- paymentMethod: 'crypto'
170
- })
171
- onDone()
172
- }
173
-
174
- return (
175
- <div className='flex flex-col gap-6 mt-6'>
176
- <div className='flex flex-col w-full'>
177
- <ContactForm form={contactForm}/>
178
- <div className='flex gap-2 grid grid-cols-3'>
179
- <Select onValueChange={(token) => {/*ONLY ETH setSelectedToken(token) */}} defaultValue='eth'>
180
- <SelectTrigger>
181
- <SelectValue defaultValue='eth' />
182
- </SelectTrigger>
183
- <SelectContent>
184
- <SelectGroup>
185
- <SelectItem value='eth'><div className='flex items-center gap-2'><Eth height={14}/>ETH</div></SelectItem>
186
- {/* <SelectItem value='btc' ><div className='flex items-center gap-2'><Btc height={14}/>BTC</div></SelectItem>
187
- <SelectItem value='usdt' ><div className='flex items-center gap-2'><Usdt height={14}/>USDT</div></SelectItem> */}
188
- </SelectGroup>
189
- </SelectContent>
190
- </Select>
191
- <div className='col-span-2'>
192
- <Input
193
- value={amount ? amount/(10**18) : amount}
194
- contentEditable={false}
195
- />
196
- <div className='relative flex items-center gap-2 -top-[32px] justify-end px-2 py-1 rounded-lg bg-muted-4 w-fit text-xs float-right mr-3'>
197
- <Eth height={10}/>
198
- ETH
199
- </div>
200
- </div>
201
- </div>
202
-
203
- {transactionStatus === 'error' ? (
204
- <h4 className='text-destructive'>There was an error while confirming the transaction.</h4>
205
- ) : transactionStatus === 'paid' ? (
206
- <h4>Waiting for transaction to be confirmed on chain.</h4>
207
- ) : transactionStatus === 'confirmed' ? (
208
- <h4>Transaction confirmed!</h4>
209
- ) : null}
210
-
211
- {transactionStatus === 'unpaid' ? (
212
- <Button
213
- onClick={() => sendPayment(amount ? amount/(10**18) : 0)}
214
- className='mx-auto w-full'
215
- disabled={loadingPrice}
216
- >
217
- Pay
218
- </Button>
219
- ) : (
220
- <Button onClick={nextStep} className='mx-auto w-full'>Continue</Button>
221
- )}
222
- </div>
223
- </div>
224
- )
225
- })
226
-
227
- export default PayWithCrypto
1
+ 'use client'
2
+
3
+ import React, { useEffect, useState } from 'react'
4
+ import { autorun } from 'mobx'
5
+ import { observer } from 'mobx-react-lite'
6
+
7
+ import { ethers } from 'ethers'
8
+
9
+ import {
10
+ Button,
11
+ Input,
12
+ Select,
13
+ SelectContent,
14
+ SelectGroup,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue,
18
+ toast
19
+ } from '@hanzo/ui/primitives'
20
+
21
+ import Eth from '../crypto-icons/eth'
22
+ import { useCommerce } from '../../../../service/context'
23
+ import type { PaymentMethodComponentProps } from '../../../../types'
24
+ import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
25
+
26
+ import ContactForm from '../contact-form'
27
+
28
+ declare global {
29
+ interface Window{
30
+ ethereum?: any
31
+ }
32
+ }
33
+
34
+ const PayWithCrypto: React.FC<PaymentMethodComponentProps> = observer(({
35
+ onDone,
36
+ transactionStatus,
37
+ setTransactionStatus,
38
+ storePaymentInfo,
39
+ contactForm
40
+ }) => {
41
+ const cmmc = useCommerce()
42
+
43
+ const [loadingPrice, setLoadingPrice] = useState(false)
44
+ //const [selectedToken, setSelectedToken] = useState('eth')
45
+ const [amount, setAmount] = useState<number>()
46
+ const [provider, setProvider] = useState<ethers.BrowserProvider>()
47
+
48
+ //const selectedToken = 'eth'
49
+
50
+ useEffect(() => {
51
+ setTransactionStatus('unpaid')
52
+ // responding to changes in user.walletAddress
53
+ return autorun(() => {
54
+ const newProvider = new ethers.BrowserProvider(window.ethereum)
55
+ setProvider(newProvider)
56
+ })
57
+ }, [])
58
+
59
+ // Get latest USD -> ETH exchange rate
60
+ useEffect(() => {
61
+ const fetchPrice = () => {
62
+ setLoadingPrice(true)
63
+ fetch(process.env.NEXT_PUBLIC_ETH_EXCHANGE_RATE_API ?? '')
64
+ .then(res => res.json())
65
+ .then((exchangeRate) => {
66
+ const oneUsdInWei = (10**18) / exchangeRate.data.amount
67
+ const usdAmountInWei = oneUsdInWei * cmmc.promoAppliedCartTotal
68
+ setAmount(usdAmountInWei)
69
+ setLoadingPrice(false)
70
+ })
71
+ }
72
+
73
+ // Call immediately on load
74
+ fetchPrice()
75
+
76
+ // Then set interval to call every 30 seconds
77
+ const interval = setInterval(fetchPrice, 30000)
78
+
79
+ return () => clearInterval(interval)
80
+ }, [cmmc.promoAppliedCartTotal])
81
+
82
+ const sendPayment = async (ether: number) => {
83
+ contactForm.handleSubmit(async () => {
84
+ // Check that we are on ethereum network
85
+ try {
86
+ await window.ethereum.request({
87
+ method: 'wallet_switchEthereumChain',
88
+ params: [{ chainId: "0x1"}],
89
+ })
90
+ const newProvider = new ethers.BrowserProvider(window.ethereum)
91
+ setProvider(newProvider)
92
+ } catch (err) {
93
+ toast('Please switch your wallet to the Ethereum network.')
94
+ return
95
+ }
96
+
97
+ try {
98
+ if (!provider) {
99
+ // :aa TODO string table
100
+ throw new Error('No crypto wallet found. Please install it.')
101
+ }
102
+
103
+ await window.ethereum.send('eth_requestAccounts')
104
+
105
+ const signer = await provider.getSigner()
106
+ ethers.getAddress(process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS ?? '')
107
+ const price = ethers.parseEther(ether.toString())
108
+ const tx = await signer.sendTransaction({
109
+ to: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
110
+ value: price
111
+ })
112
+ console.log({ ether, addr: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS })
113
+ console.log('tx', tx)
114
+ setTransactionStatus('paid')
115
+ await storePaymentInfo({
116
+ ether,
117
+ transactionHash: tx.hash,
118
+ to: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
119
+ paymentMethod: 'crypto'
120
+ })
121
+
122
+ provider.waitForTransaction(tx.hash)
123
+ .then(async (receipt) => {
124
+ console.log(receipt)
125
+ await storePaymentInfo({
126
+ ether,
127
+ addr: process.env.NEXT_PUBLIC_ETH_PAYMENT_ADDRESS,
128
+ receipt,
129
+ paymentMethod: 'crypto'
130
+ })
131
+ sendGAEvent('purchase', {
132
+ transaction_id: tx.hash,
133
+ value: price,
134
+ currency: 'ETH',
135
+ items: cmmc.cartItems.map((item) => ({
136
+ item_id: item.sku,
137
+ item_name: item.title,
138
+ item_category: item.familyId,
139
+ price: item.price,
140
+ quantity: item.quantity
141
+ })),
142
+ })
143
+ sendFBEvent('Purchase', {
144
+ content_ids: cmmc.cartItems.map((item) => item.sku),
145
+ contents: cmmc.cartItems.map(item => ({
146
+ id: item.sku,
147
+ quantity: item.quantity
148
+ })),
149
+ num_items: cmmc.cartItems.length,
150
+ value: price,
151
+ currency: 'ETH',
152
+ })
153
+ setTransactionStatus('confirmed')
154
+ })
155
+ .catch((error) => {
156
+ console.error(error)
157
+ setTransactionStatus('error')
158
+ })
159
+ } catch (err) {
160
+ console.log(err)
161
+ // :aa TODO string table
162
+ toast('Not enough funds in your wallet')
163
+ }
164
+ })()
165
+ }
166
+
167
+ const nextStep = async () => {
168
+ await storePaymentInfo({
169
+ paymentMethod: 'crypto'
170
+ })
171
+ onDone()
172
+ }
173
+
174
+ return (
175
+ <div className='flex flex-col gap-6 mt-6'>
176
+ <div className='flex flex-col w-full'>
177
+ <ContactForm form={contactForm}/>
178
+ <div className='flex gap-2 grid grid-cols-3'>
179
+ <Select onValueChange={(token) => {/*ONLY ETH setSelectedToken(token) */}} defaultValue='eth'>
180
+ <SelectTrigger>
181
+ <SelectValue defaultValue='eth' />
182
+ </SelectTrigger>
183
+ <SelectContent>
184
+ <SelectGroup>
185
+ <SelectItem value='eth'><div className='flex items-center gap-2'><Eth height={14}/>ETH</div></SelectItem>
186
+ {/* <SelectItem value='btc' ><div className='flex items-center gap-2'><Btc height={14}/>BTC</div></SelectItem>
187
+ <SelectItem value='usdt' ><div className='flex items-center gap-2'><Usdt height={14}/>USDT</div></SelectItem> */}
188
+ </SelectGroup>
189
+ </SelectContent>
190
+ </Select>
191
+ <div className='col-span-2'>
192
+ <Input
193
+ value={amount ? amount/(10**18) : amount}
194
+ contentEditable={false}
195
+ />
196
+ <div className='relative flex items-center gap-2 -top-[32px] justify-end px-2 py-1 rounded-lg bg-muted-4 w-fit text-xs float-right mr-3'>
197
+ <Eth height={10}/>
198
+ ETH
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ {transactionStatus === 'error' ? (
204
+ <h4 className='text-destructive'>There was an error while confirming the transaction.</h4>
205
+ ) : transactionStatus === 'paid' ? (
206
+ <h4>Waiting for transaction to be confirmed on chain.</h4>
207
+ ) : transactionStatus === 'confirmed' ? (
208
+ <h4>Transaction confirmed!</h4>
209
+ ) : null}
210
+
211
+ {transactionStatus === 'unpaid' ? (
212
+ <Button
213
+ onClick={() => sendPayment(amount ? amount/(10**18) : 0)}
214
+ className='mx-auto w-full'
215
+ disabled={loadingPrice}
216
+ >
217
+ Pay
218
+ </Button>
219
+ ) : (
220
+ <Button onClick={nextStep} className='mx-auto w-full'>Continue</Button>
221
+ )}
222
+ </div>
223
+ </div>
224
+ )
225
+ })
226
+
227
+ export default PayWithCrypto
@@ -1,23 +1,23 @@
1
- import type { PaymentMethodDesc } from '../../../../types'
2
-
3
- import Card from './card'
4
- import Crypto from './crypto'
5
- import BankTransfer from './bank-transfer'
6
-
7
- export default [
8
- {
9
- value: 'card',
10
- label: 'Card',
11
- Comp: Card
12
- },
13
- {
14
- value: 'crypto',
15
- label: 'Wallet',
16
- Comp: Crypto
17
- },
18
- {
19
- value: 'bank',
20
- label: 'Bank Wire',
21
- Comp: BankTransfer
22
- },
1
+ import type { PaymentMethodDesc } from '../../../../types'
2
+
3
+ import Card from './card'
4
+ import Crypto from './crypto'
5
+ import BankTransfer from './bank-transfer'
6
+
7
+ export default [
8
+ {
9
+ value: 'card',
10
+ label: 'Card',
11
+ Comp: Card
12
+ },
13
+ {
14
+ value: 'crypto',
15
+ label: 'Wallet',
16
+ Comp: Crypto
17
+ },
18
+ {
19
+ value: 'bank',
20
+ label: 'Bank Wire',
21
+ Comp: BankTransfer
22
+ },
23
23
  ] satisfies PaymentMethodDesc[]