@libreapps/commerce 7.5.1

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 (327) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +13 -0
  3. package/LICENSE.md +21 -0
  4. package/components/Icons.tsx +35 -0
  5. package/components/add-to-cart-widget.tsx +183 -0
  6. package/components/buy/buy-card.tsx +259 -0
  7. package/components/buy/carousel-buy-card.tsx +242 -0
  8. package/components/buy/multi-family/all-variants-carousel.tsx +261 -0
  9. package/components/buy/multi-family/family-carousel/index.tsx +77 -0
  10. package/components/buy/multi-family/family-carousel/slide.tsx +83 -0
  11. package/components/buy/multi-family/family-carousel/state.ts +87 -0
  12. package/components/buy/multi-family/index.ts +2 -0
  13. package/components/buy/single-family-selector.tsx +90 -0
  14. package/components/buy/title-and-byline.tsx +25 -0
  15. package/components/cart/cart-panel/cart-line-item.tsx +76 -0
  16. package/components/cart/cart-panel/index.tsx +154 -0
  17. package/components/cart/cart-panel/promo-code.tsx +109 -0
  18. package/components/cart/cart-panel/total-area.tsx +60 -0
  19. package/components/checkout/payment-step-form/card-icon-row.tsx +26 -0
  20. package/components/checkout/payment-step-form/card-icons/amex.tsx +32 -0
  21. package/components/checkout/payment-step-form/card-icons/diners-club.tsx +13 -0
  22. package/components/checkout/payment-step-form/card-icons/discover.tsx +25 -0
  23. package/components/checkout/payment-step-form/card-icons/jcb.tsx +26 -0
  24. package/components/checkout/payment-step-form/card-icons/mastercard.tsx +27 -0
  25. package/components/checkout/payment-step-form/card-icons/visa.tsx +25 -0
  26. package/components/checkout/payment-step-form/cc-button.tsx +17 -0
  27. package/components/checkout/payment-step-form/contact-form.tsx +50 -0
  28. package/components/checkout/payment-step-form/crypto-icons/btc.tsx +11 -0
  29. package/components/checkout/payment-step-form/crypto-icons/eth.tsx +20 -0
  30. package/components/checkout/payment-step-form/crypto-icons/usdt.tsx +13 -0
  31. package/components/checkout/payment-step-form/index.tsx +122 -0
  32. package/components/checkout/payment-step-form/methods/bank-transfer.tsx +79 -0
  33. package/components/checkout/payment-step-form/methods/card.tsx +232 -0
  34. package/components/checkout/payment-step-form/methods/crypto.tsx +227 -0
  35. package/components/checkout/payment-step-form/methods/index.ts +23 -0
  36. package/components/checkout/shipping-step-form.tsx +175 -0
  37. package/components/index.ts +11 -0
  38. package/components/item/product-card.tsx +48 -0
  39. package/components/item-selector/button.tsx +188 -0
  40. package/components/item-selector/carousel/index.tsx +197 -0
  41. package/components/item-selector/carousel/slider.tsx +40 -0
  42. package/components/item-selector/index.ts +5 -0
  43. package/components/item-selector/quantity-indicator.tsx +48 -0
  44. package/components/node-tabs/index.tsx +91 -0
  45. package/components/node-tabs/node-image.tsx +31 -0
  46. package/dist/components/Icons.d.ts +18 -0
  47. package/dist/components/Icons.js +19 -0
  48. package/dist/components/Icons.js.map +1 -0
  49. package/dist/components/add-to-cart-widget.d.ts +11 -0
  50. package/dist/components/add-to-cart-widget.js +85 -0
  51. package/dist/components/add-to-cart-widget.js.map +1 -0
  52. package/dist/components/buy/buy-card.d.ts +30 -0
  53. package/dist/components/buy/buy-card.js +109 -0
  54. package/dist/components/buy/buy-card.js.map +1 -0
  55. package/dist/components/buy/carousel-buy-card.d.ts +12 -0
  56. package/dist/components/buy/carousel-buy-card.js +94 -0
  57. package/dist/components/buy/carousel-buy-card.js.map +1 -0
  58. package/dist/components/buy/multi-family/all-variants-carousel.d.ts +4 -0
  59. package/dist/components/buy/multi-family/all-variants-carousel.js +115 -0
  60. package/dist/components/buy/multi-family/all-variants-carousel.js.map +1 -0
  61. package/dist/components/buy/multi-family/family-carousel/index.d.ts +4 -0
  62. package/dist/components/buy/multi-family/family-carousel/index.js +27 -0
  63. package/dist/components/buy/multi-family/family-carousel/index.js.map +1 -0
  64. package/dist/components/buy/multi-family/family-carousel/slide.d.ts +11 -0
  65. package/dist/components/buy/multi-family/family-carousel/slide.js +35 -0
  66. package/dist/components/buy/multi-family/family-carousel/slide.js.map +1 -0
  67. package/dist/components/buy/multi-family/family-carousel/state.d.ts +20 -0
  68. package/dist/components/buy/multi-family/family-carousel/state.js +59 -0
  69. package/dist/components/buy/multi-family/family-carousel/state.js.map +1 -0
  70. package/dist/components/buy/multi-family/index.d.ts +2 -0
  71. package/dist/components/buy/multi-family/index.js +3 -0
  72. package/dist/components/buy/multi-family/index.js.map +1 -0
  73. package/dist/components/buy/single-family-selector.d.ts +15 -0
  74. package/dist/components/buy/single-family-selector.js +28 -0
  75. package/dist/components/buy/single-family-selector.js.map +1 -0
  76. package/dist/components/buy/title-and-byline.d.ts +8 -0
  77. package/dist/components/buy/title-and-byline.js +7 -0
  78. package/dist/components/buy/title-and-byline.js.map +1 -0
  79. package/dist/components/cart/cart-panel/cart-line-item.d.ts +11 -0
  80. package/dist/components/cart/cart-panel/cart-line-item.js +25 -0
  81. package/dist/components/cart/cart-panel/cart-line-item.js.map +1 -0
  82. package/dist/components/cart/cart-panel/index.d.ts +19 -0
  83. package/dist/components/cart/cart-panel/index.js +65 -0
  84. package/dist/components/cart/cart-panel/index.js.map +1 -0
  85. package/dist/components/cart/cart-panel/promo-code.d.ts +4 -0
  86. package/dist/components/cart/cart-panel/promo-code.js +62 -0
  87. package/dist/components/cart/cart-panel/promo-code.js.map +1 -0
  88. package/dist/components/cart/cart-panel/total-area.d.ts +7 -0
  89. package/dist/components/cart/cart-panel/total-area.js +14 -0
  90. package/dist/components/cart/cart-panel/total-area.js.map +1 -0
  91. package/dist/components/checkout/payment-step-form/card-icon-row.d.ts +2 -0
  92. package/dist/components/checkout/payment-step-form/card-icon-row.js +14 -0
  93. package/dist/components/checkout/payment-step-form/card-icon-row.js.map +1 -0
  94. package/dist/components/checkout/payment-step-form/card-icons/amex.d.ts +4 -0
  95. package/dist/components/checkout/payment-step-form/card-icons/amex.js +6 -0
  96. package/dist/components/checkout/payment-step-form/card-icons/amex.js.map +1 -0
  97. package/dist/components/checkout/payment-step-form/card-icons/diners-club.d.ts +4 -0
  98. package/dist/components/checkout/payment-step-form/card-icons/diners-club.js +6 -0
  99. package/dist/components/checkout/payment-step-form/card-icons/diners-club.js.map +1 -0
  100. package/dist/components/checkout/payment-step-form/card-icons/discover.d.ts +4 -0
  101. package/dist/components/checkout/payment-step-form/card-icons/discover.js +6 -0
  102. package/dist/components/checkout/payment-step-form/card-icons/discover.js.map +1 -0
  103. package/dist/components/checkout/payment-step-form/card-icons/jcb.d.ts +4 -0
  104. package/dist/components/checkout/payment-step-form/card-icons/jcb.js +6 -0
  105. package/dist/components/checkout/payment-step-form/card-icons/jcb.js.map +1 -0
  106. package/dist/components/checkout/payment-step-form/card-icons/mastercard.d.ts +4 -0
  107. package/dist/components/checkout/payment-step-form/card-icons/mastercard.js +6 -0
  108. package/dist/components/checkout/payment-step-form/card-icons/mastercard.js.map +1 -0
  109. package/dist/components/checkout/payment-step-form/card-icons/visa.d.ts +4 -0
  110. package/dist/components/checkout/payment-step-form/card-icons/visa.js +6 -0
  111. package/dist/components/checkout/payment-step-form/card-icons/visa.js.map +1 -0
  112. package/dist/components/checkout/payment-step-form/cc-button.d.ts +3 -0
  113. package/dist/components/checkout/payment-step-form/cc-button.js +6 -0
  114. package/dist/components/checkout/payment-step-form/cc-button.js.map +1 -0
  115. package/dist/components/checkout/payment-step-form/contact-form.d.ts +5 -0
  116. package/dist/components/checkout/payment-step-form/contact-form.js +6 -0
  117. package/dist/components/checkout/payment-step-form/contact-form.js.map +1 -0
  118. package/dist/components/checkout/payment-step-form/crypto-icons/btc.d.ts +4 -0
  119. package/dist/components/checkout/payment-step-form/crypto-icons/btc.js +6 -0
  120. package/dist/components/checkout/payment-step-form/crypto-icons/btc.js.map +1 -0
  121. package/dist/components/checkout/payment-step-form/crypto-icons/eth.d.ts +4 -0
  122. package/dist/components/checkout/payment-step-form/crypto-icons/eth.js +6 -0
  123. package/dist/components/checkout/payment-step-form/crypto-icons/eth.js.map +1 -0
  124. package/dist/components/checkout/payment-step-form/crypto-icons/usdt.d.ts +4 -0
  125. package/dist/components/checkout/payment-step-form/crypto-icons/usdt.js +6 -0
  126. package/dist/components/checkout/payment-step-form/crypto-icons/usdt.js.map +1 -0
  127. package/dist/components/checkout/payment-step-form/index.d.ts +4 -0
  128. package/dist/components/checkout/payment-step-form/index.js +77 -0
  129. package/dist/components/checkout/payment-step-form/index.js.map +1 -0
  130. package/dist/components/checkout/payment-step-form/methods/bank-transfer.d.ts +4 -0
  131. package/dist/components/checkout/payment-step-form/methods/bank-transfer.js +24 -0
  132. package/dist/components/checkout/payment-step-form/methods/bank-transfer.js.map +1 -0
  133. package/dist/components/checkout/payment-step-form/methods/card.d.ts +4 -0
  134. package/dist/components/checkout/payment-step-form/methods/card.js +160 -0
  135. package/dist/components/checkout/payment-step-form/methods/card.js.map +1 -0
  136. package/dist/components/checkout/payment-step-form/methods/crypto.d.ts +9 -0
  137. package/dist/components/checkout/payment-step-form/methods/crypto.js +137 -0
  138. package/dist/components/checkout/payment-step-form/methods/crypto.js.map +1 -0
  139. package/dist/components/checkout/payment-step-form/methods/index.d.ts +6 -0
  140. package/dist/components/checkout/payment-step-form/methods/index.js +21 -0
  141. package/dist/components/checkout/payment-step-form/methods/index.js.map +1 -0
  142. package/dist/components/checkout/shipping-step-form.d.ts +3 -0
  143. package/dist/components/checkout/shipping-step-form.js +53 -0
  144. package/dist/components/checkout/shipping-step-form.js.map +1 -0
  145. package/dist/components/index.d.ts +8 -0
  146. package/dist/components/index.js +9 -0
  147. package/dist/components/index.js.map +1 -0
  148. package/dist/components/item/product-card.d.ts +7 -0
  149. package/dist/components/item/product-card.js +9 -0
  150. package/dist/components/item/product-card.js.map +1 -0
  151. package/dist/components/item-selector/button.d.ts +4 -0
  152. package/dist/components/item-selector/button.js +47 -0
  153. package/dist/components/item-selector/button.js.map +1 -0
  154. package/dist/components/item-selector/carousel/index.d.ts +12 -0
  155. package/dist/components/item-selector/carousel/index.js +74 -0
  156. package/dist/components/item-selector/carousel/index.js.map +1 -0
  157. package/dist/components/item-selector/carousel/slider.d.ts +8 -0
  158. package/dist/components/item-selector/carousel/slider.js +12 -0
  159. package/dist/components/item-selector/carousel/slider.js.map +1 -0
  160. package/dist/components/item-selector/index.d.ts +2 -0
  161. package/dist/components/item-selector/index.js +3 -0
  162. package/dist/components/item-selector/index.js.map +1 -0
  163. package/dist/components/item-selector/quantity-indicator.d.ts +9 -0
  164. package/dist/components/item-selector/quantity-indicator.js +16 -0
  165. package/dist/components/item-selector/quantity-indicator.js.map +1 -0
  166. package/dist/components/node-tabs/index.d.ts +14 -0
  167. package/dist/components/node-tabs/index.js +42 -0
  168. package/dist/components/node-tabs/index.js.map +1 -0
  169. package/dist/components/node-tabs/node-image.d.ts +6 -0
  170. package/dist/components/node-tabs/node-image.js +13 -0
  171. package/dist/components/node-tabs/node-image.js.map +1 -0
  172. package/dist/index.d.ts +5 -0
  173. package/dist/index.js +5 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/service/context.d.ts +8 -0
  176. package/dist/service/context.js +19 -0
  177. package/dist/service/context.js.map +1 -0
  178. package/dist/service/debug.d.ts +10 -0
  179. package/dist/service/debug.js +30 -0
  180. package/dist/service/debug.js.map +1 -0
  181. package/dist/service/impls/standalone/actual-line-item.d.ts +40 -0
  182. package/dist/service/impls/standalone/actual-line-item.js +84 -0
  183. package/dist/service/impls/standalone/actual-line-item.js.map +1 -0
  184. package/dist/service/impls/standalone/get-instance.d.ts +2 -0
  185. package/dist/service/impls/standalone/get-instance.js +39 -0
  186. package/dist/service/impls/standalone/get-instance.js.map +1 -0
  187. package/dist/service/impls/standalone/index.d.ts +67 -0
  188. package/dist/service/impls/standalone/index.js +416 -0
  189. package/dist/service/impls/standalone/index.js.map +1 -0
  190. package/dist/service/impls/standalone/order/firebase.d.ts +2 -0
  191. package/dist/service/impls/standalone/order/firebase.js +13 -0
  192. package/dist/service/impls/standalone/order/firebase.js.map +1 -0
  193. package/dist/service/impls/standalone/order/index.d.ts +24 -0
  194. package/dist/service/impls/standalone/order/index.js +61 -0
  195. package/dist/service/impls/standalone/order/index.js.map +1 -0
  196. package/dist/service/impls/standalone/persistence.d.ts +4 -0
  197. package/dist/service/impls/standalone/persistence.js +22 -0
  198. package/dist/service/impls/standalone/persistence.js.map +1 -0
  199. package/dist/service/path-utils.d.ts +7 -0
  200. package/dist/service/path-utils.js +16 -0
  201. package/dist/service/path-utils.js.map +1 -0
  202. package/dist/service/sep.d.ts +6 -0
  203. package/dist/service/sep.js +6 -0
  204. package/dist/service/sep.js.map +1 -0
  205. package/dist/types/category-node.d.ts +36 -0
  206. package/dist/types/category-node.js +2 -0
  207. package/dist/types/category-node.js.map +1 -0
  208. package/dist/types/checkout.d.ts +33 -0
  209. package/dist/types/checkout.js +2 -0
  210. package/dist/types/checkout.js.map +1 -0
  211. package/dist/types/commerce-config.d.ts +11 -0
  212. package/dist/types/commerce-config.js +2 -0
  213. package/dist/types/commerce-config.js.map +1 -0
  214. package/dist/types/commerce-service.d.ts +109 -0
  215. package/dist/types/commerce-service.js +2 -0
  216. package/dist/types/commerce-service.js.map +1 -0
  217. package/dist/types/family.d.ts +16 -0
  218. package/dist/types/family.js +2 -0
  219. package/dist/types/family.js.map +1 -0
  220. package/dist/types/index.d.ts +13 -0
  221. package/dist/types/index.js +8 -0
  222. package/dist/types/index.js.map +1 -0
  223. package/dist/types/item-selector.d.ts +72 -0
  224. package/dist/types/item-selector.js +2 -0
  225. package/dist/types/item-selector.js.map +1 -0
  226. package/dist/types/line-item.d.ts +14 -0
  227. package/dist/types/line-item.js +2 -0
  228. package/dist/types/line-item.js.map +1 -0
  229. package/dist/types/multi-family-selector-props.d.ts +16 -0
  230. package/dist/types/multi-family-selector-props.js +2 -0
  231. package/dist/types/multi-family-selector-props.js.map +1 -0
  232. package/dist/types/product.d.ts +15 -0
  233. package/dist/types/product.js +2 -0
  234. package/dist/types/product.js.map +1 -0
  235. package/dist/types/promo.d.ts +7 -0
  236. package/dist/types/promo.js +2 -0
  237. package/dist/types/promo.js.map +1 -0
  238. package/dist/types/selection-ui-specifier.d.ts +40 -0
  239. package/dist/types/selection-ui-specifier.js +2 -0
  240. package/dist/types/selection-ui-specifier.js.map +1 -0
  241. package/dist/types/string-mutator.d.ts +9 -0
  242. package/dist/types/string-mutator.js +2 -0
  243. package/dist/types/string-mutator.js.map +1 -0
  244. package/dist/types/token-separators.d.ts +6 -0
  245. package/dist/types/token-separators.js +2 -0
  246. package/dist/types/token-separators.js.map +1 -0
  247. package/dist/util/analytics.d.ts +9 -0
  248. package/dist/util/analytics.js +10 -0
  249. package/dist/util/analytics.js.map +1 -0
  250. package/dist/util/countries.d.ts +7 -0
  251. package/dist/util/countries.js +197 -0
  252. package/dist/util/countries.js.map +1 -0
  253. package/dist/util/error.d.ts +1 -0
  254. package/dist/util/error.js +22 -0
  255. package/dist/util/error.js.map +1 -0
  256. package/dist/util/index.d.ts +15 -0
  257. package/dist/util/index.js +54 -0
  258. package/dist/util/index.js.map +1 -0
  259. package/dist/util/item-selector-options-accessor.d.ts +3 -0
  260. package/dist/util/item-selector-options-accessor.js +27 -0
  261. package/dist/util/item-selector-options-accessor.js.map +1 -0
  262. package/dist/util/line-item-ref.d.ts +8 -0
  263. package/dist/util/line-item-ref.js +15 -0
  264. package/dist/util/line-item-ref.js.map +1 -0
  265. package/dist/util/multi-family-selector-options-accessor.d.ts +3 -0
  266. package/dist/util/multi-family-selector-options-accessor.js +11 -0
  267. package/dist/util/multi-family-selector-options-accessor.js.map +1 -0
  268. package/dist/util/obs-string-mutator.d.ts +8 -0
  269. package/dist/util/obs-string-mutator.js +15 -0
  270. package/dist/util/obs-string-mutator.js.map +1 -0
  271. package/dist/util/product-media-accessor.d.ts +29 -0
  272. package/dist/util/product-media-accessor.js +22 -0
  273. package/dist/util/product-media-accessor.js.map +1 -0
  274. package/dist/util/promo-codes.d.ts +3 -0
  275. package/dist/util/promo-codes.js +100 -0
  276. package/dist/util/promo-codes.js.map +1 -0
  277. package/dist/util/selection-ui-specifiers.d.ts +3 -0
  278. package/dist/util/selection-ui-specifiers.js +24 -0
  279. package/dist/util/selection-ui-specifiers.js.map +1 -0
  280. package/dist/util/square-payment.d.ts +7 -0
  281. package/dist/util/square-payment.js +37 -0
  282. package/dist/util/square-payment.js.map +1 -0
  283. package/dist/util/use-sync-sku-param-w-current-item.d.ts +2 -0
  284. package/dist/util/use-sync-sku-param-w-current-item.js +61 -0
  285. package/dist/util/use-sync-sku-param-w-current-item.js.map +1 -0
  286. package/index.ts +13 -0
  287. package/libreapps-ui.d.ts +108 -0
  288. package/package.json +67 -0
  289. package/service/context.tsx +45 -0
  290. package/service/debug.ts +41 -0
  291. package/service/impls/standalone/actual-line-item.ts +136 -0
  292. package/service/impls/standalone/get-instance.ts +64 -0
  293. package/service/impls/standalone/index.ts +579 -0
  294. package/service/impls/standalone/order/firebase.ts +14 -0
  295. package/service/impls/standalone/order/index.ts +129 -0
  296. package/service/impls/standalone/persistence.ts +33 -0
  297. package/service/path-utils.ts +26 -0
  298. package/service/sep.ts +7 -0
  299. package/tsconfig.json +17 -0
  300. package/types/README.md +2 -0
  301. package/types/category-node.ts +50 -0
  302. package/types/checkout.ts +47 -0
  303. package/types/commerce-config.ts +13 -0
  304. package/types/commerce-service.ts +128 -0
  305. package/types/family.ts +26 -0
  306. package/types/index.ts +15 -0
  307. package/types/item-selector.ts +97 -0
  308. package/types/line-item.ts +29 -0
  309. package/types/multi-family-selector-props.ts +20 -0
  310. package/types/product.ts +21 -0
  311. package/types/promo.ts +10 -0
  312. package/types/selection-ui-specifier.ts +52 -0
  313. package/types/string-mutator.ts +14 -0
  314. package/types/token-separators.ts +7 -0
  315. package/util/analytics.ts +21 -0
  316. package/util/countries.ts +196 -0
  317. package/util/error.ts +34 -0
  318. package/util/index.ts +71 -0
  319. package/util/item-selector-options-accessor.ts +35 -0
  320. package/util/line-item-ref.ts +23 -0
  321. package/util/multi-family-selector-options-accessor.ts +15 -0
  322. package/util/obs-string-mutator.ts +22 -0
  323. package/util/product-media-accessor.ts +58 -0
  324. package/util/promo-codes.ts +106 -0
  325. package/util/selection-ui-specifiers.ts +30 -0
  326. package/util/square-payment.ts +50 -0
  327. package/util/use-sync-sku-param-w-current-item.ts +88 -0
@@ -0,0 +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 '@libreapps/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
@@ -0,0 +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
+ },
23
+ ] satisfies PaymentMethodDesc[]
@@ -0,0 +1,175 @@
1
+ 'use client'
2
+
3
+ import * as z from 'zod'
4
+ import { useForm } from 'react-hook-form'
5
+ import { zodResolver } from '@hookform/resolvers/zod'
6
+
7
+ import {
8
+ Input,
9
+ Button,
10
+ Select,
11
+ SelectContent,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue
15
+ } from '@libreapps/ui/primitives'
16
+
17
+ import {
18
+ Form,
19
+ FormControl,
20
+ FormField,
21
+ FormItem,
22
+ FormMessage,
23
+ } from '@libreapps/ui/form'
24
+
25
+ import { useCommerce } from '../../service/context'
26
+
27
+ import countries from '../../util/countries'
28
+ import { sendGAEvent } from '../../util/analytics'
29
+
30
+ import type { CheckoutStepComponentProps } from '../../types'
31
+
32
+ const shippingFormSchema = z.object({
33
+ addressLine1: z.string().min(2, 'Address must be at least 2 characters.'),
34
+ addressLine2: z.string().optional(),
35
+ zipCode: z.string().min(2, 'Zip code is invalid.'),
36
+ city: z.string().min(2, 'City is invalid.'),
37
+ state: z.string().optional(),
38
+ country: z.string().min(2, 'Country is invalid.'),
39
+ })
40
+
41
+ const ShippingStepForm: React.FC<CheckoutStepComponentProps> = ({
42
+ orderId,
43
+ onDone
44
+ }) => {
45
+ const cmmc = useCommerce()
46
+
47
+ const shippingForm = useForm<z.infer<typeof shippingFormSchema>>({
48
+ resolver: zodResolver(shippingFormSchema),
49
+ defaultValues: {
50
+ addressLine1: '',
51
+ addressLine2: '',
52
+ zipCode: '',
53
+ city: '',
54
+ state: '',
55
+ country: '',
56
+ },
57
+ })
58
+
59
+ const onSubmit = async (values: z.infer<typeof shippingFormSchema>) => {
60
+ if (orderId) {
61
+ await cmmc.updateOrderShippingInfo(orderId, values)
62
+ }
63
+ sendGAEvent('add_shipping_info', {
64
+ items: cmmc.cartItems.map((item) => ({
65
+ item_id: item.sku,
66
+ item_name: item.title,
67
+ item_category: item.familyId,
68
+ price: item.price,
69
+ quantity: item.quantity
70
+ })),
71
+ num_items: cmmc.cartItems.length,
72
+ value: cmmc.cartTotal,
73
+ currency: 'USD',
74
+ })
75
+ onDone()
76
+ }
77
+
78
+ return (
79
+ <Form {...shippingForm}>
80
+ <form onSubmit={shippingForm.handleSubmit(onSubmit)} className='text-left'>
81
+ <div className='flex flex-col gap-4'>
82
+ <FormField
83
+ control={shippingForm.control}
84
+ name='addressLine1'
85
+ render={({ field }) => (
86
+ <FormItem className='space-y-1 w-full'>
87
+ <FormControl>
88
+ <Input {...field} placeholder='Address line 1'/>
89
+ </FormControl>
90
+ <FormMessage />
91
+ </FormItem>
92
+ )}
93
+ />
94
+ <FormField
95
+ control={shippingForm.control}
96
+ name='addressLine2'
97
+ render={({ field }) => (
98
+ <FormItem className='space-y-1 w-full'>
99
+ <FormControl>
100
+ <Input {...field} placeholder='Address line 2 (optional)'/>
101
+ </FormControl>
102
+ <FormMessage />
103
+ </FormItem>
104
+ )}
105
+ />
106
+ <div className='flex gap-4 items-end'>
107
+ <FormField
108
+ control={shippingForm.control}
109
+ name='zipCode'
110
+ render={({ field }) => (
111
+ <FormItem className='space-y-1 w-full'>
112
+ <FormControl>
113
+ <Input {...field} placeholder='Zip code'/>
114
+ </FormControl>
115
+ <FormMessage />
116
+ </FormItem>
117
+ )}
118
+ />
119
+ <FormField
120
+ control={shippingForm.control}
121
+ name='city'
122
+ render={({ field }) => (
123
+ <FormItem className='space-y-1 w-full'>
124
+ <FormControl>
125
+ <Input {...field} placeholder='City'/>
126
+ </FormControl>
127
+ <FormMessage />
128
+ </FormItem>
129
+ )}
130
+ />
131
+ </div>
132
+ <div className='flex gap-4 items-start'>
133
+ <FormField
134
+ control={shippingForm.control}
135
+ name='state'
136
+ render={({ field }) => (
137
+ <FormItem className='space-y-1 w-full'>
138
+ <FormControl>
139
+ <Input {...field} placeholder='State (optional)'/>
140
+ </FormControl>
141
+ <FormMessage />
142
+ </FormItem>
143
+ )}
144
+ />
145
+ <FormField
146
+ control={shippingForm.control}
147
+ name='country'
148
+ render={({ field }) => (
149
+ <FormItem className='space-y-1 w-full'>
150
+ <Select onValueChange={field.onChange} defaultValue={field.value}>
151
+ <FormControl>
152
+ <SelectTrigger className='bg-level-1 border-muted-4'>
153
+ <SelectValue placeholder='Country' />
154
+ </SelectTrigger>
155
+ </FormControl>
156
+ <SelectContent>
157
+ {countries.map((country) => (
158
+ <SelectItem key={country.id} value={country.name}>
159
+ {country.name}
160
+ </SelectItem>
161
+ ))}
162
+ </SelectContent>
163
+ </Select>
164
+ </FormItem>
165
+ )}
166
+ />
167
+ </div>
168
+ </div>
169
+ <Button type='submit' className='mx-auto w-full mt-4'>Confirm order</Button>
170
+ </form>
171
+ </Form>
172
+ )
173
+ }
174
+
175
+ export default ShippingStepForm
@@ -0,0 +1,11 @@
1
+ export { default as AddToCartWidget } from './add-to-cart-widget'
2
+ export { default as CarouselBuyCard } from './buy/carousel-buy-card'
3
+
4
+ export { default as CartPanel } from './cart/cart-panel'
5
+
6
+ export { default as Icons } from './Icons'
7
+ export { default as PaymentStepForm } from './checkout/payment-step-form'
8
+ export { default as ProductCard } from './item/product-card'
9
+ export { default as ShippingStepForm } from './checkout/shipping-step-form'
10
+
11
+ export * from './item-selector'
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardFooter,
7
+ CardHeader,
8
+ CardTitle,
9
+ MediaStack
10
+ } from '@libreapps/ui/primitives'
11
+
12
+ import { cn } from '@libreapps/ui/util'
13
+
14
+ import { formatCurrencyValue } from '../../util'
15
+ import type { LineItem } from '../../types'
16
+
17
+ import AddToCartWidget from '../add-to-cart-widget'
18
+
19
+ interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
20
+ item: LineItem
21
+ }
22
+
23
+ const ProductCard: React.FC<ProductCardProps> = ({
24
+ item,
25
+ className,
26
+ ...props
27
+ }) => (
28
+ <Card
29
+ className={cn('max-h-[360px] lg:min-w-[200px] max-w-[260px] overflow-hidden', className)}
30
+ {...props}
31
+ >
32
+ <CardHeader className='w-full border-b p-6 min-h-[180px] max-h-[240px] relative'>
33
+ <MediaStack media={item} constrainTo={{w: 700, h:700}} clx='p-6' />
34
+ </CardHeader>
35
+ <CardContent className='grid gap-2.5 p-4'>
36
+ <CardTitle className='text-sm sm:text-base flex flex-col justify-start items-center line-clap-3'>
37
+ {item.title.split(', ').map((e, i) => (<p key={i}>{e}</p>))}
38
+ <p className='mt-1 font-semibold'>{formatCurrencyValue(item.price)}</p>
39
+ </CardTitle>
40
+ </CardContent>
41
+ <CardFooter className='p-4 flex flex-row justify-center'>
42
+ <AddToCartWidget item={item} />
43
+ </CardFooter>
44
+ </Card>
45
+ )
46
+
47
+
48
+ export default ProductCard
@@ -0,0 +1,188 @@
1
+ 'use client'
2
+ import React from 'react'
3
+ import { observer } from 'mobx-react-lite'
4
+
5
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
6
+
7
+ import {
8
+ Image,
9
+ Label,
10
+ RadioGroup,
11
+ ScrollArea
12
+ } from '@libreapps/ui/primitives'
13
+ import { cn } from '@libreapps/ui/util'
14
+ import type { Dimensions } from '@libreapps/ui/types'
15
+
16
+ import type { ItemSelectorProps, LineItem } from '../../types'
17
+ import { accessItemOptions, formatCurrencyValue } from '../../util'
18
+
19
+ import QuantityIndicator from './quantity-indicator'
20
+
21
+ const DEFAULT_CONSTRAINT = {h: 36, w: 72} // // Apple suggest 42px for clickability
22
+
23
+ const ImageRadioGroupItem = React.forwardRef<
24
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
25
+ Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, 'value' | 'id'> & {
26
+ item: LineItem,
27
+ constrainTo: Dimensions
28
+ }
29
+ >(({
30
+ item,
31
+ constrainTo,
32
+ className,
33
+ ...props
34
+ }, ref) => {
35
+
36
+ const img = item.optionImg ? item.optionImg : item.img
37
+
38
+ return (
39
+ <RadioGroupPrimitive.Item
40
+ ref={ref}
41
+ data-vaul-no-drag
42
+ className={cn(
43
+ 'ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',
44
+ 'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
45
+ className,
46
+ 'overflow-hidden',
47
+ img?.rounded ? `rounded-${img.rounded}` : 'rounded-sm'
48
+ )}
49
+ {...props}
50
+ id={item.sku}
51
+ value={item.sku}
52
+ >
53
+ {img ? (
54
+ <Image def={img} constrainTo={constrainTo} preload className=''/>
55
+ ) : ( // placeholder so things align
56
+ <div style={{height: constrainTo.h, width: constrainTo.w}}/>
57
+ )}
58
+ </RadioGroupPrimitive.Item>
59
+ )
60
+ })
61
+ ImageRadioGroupItem.displayName = 'ImageRadioGroupItem'
62
+
63
+ const ButtonItemSelector: React.FC<ItemSelectorProps> = observer(({
64
+ items,
65
+ selectedItemRef: itemRef,
66
+ selectSku,
67
+ clx='',
68
+ itemClx='',
69
+ soleItemClx='',
70
+ scrollable=false,
71
+ mobile=false,
72
+ options={}
73
+ }) => {
74
+
75
+ const {
76
+ showPrice,
77
+ showQuantity,
78
+ showFamilyInOption,
79
+ buttonType,
80
+ horizButtons,
81
+ showButtonIfOnlyOne
82
+ } = accessItemOptions(options)
83
+
84
+ const showImage = buttonType !== 'text'
85
+ const showText = buttonType !== 'image'
86
+ const showBoth = buttonType === 'image-and-text'
87
+
88
+ const labelAndPrice = (item : LineItem) => (
89
+ (showFamilyInOption ? (item.familyTitle + ', ' + item.optionLabel) : item.optionLabel) +
90
+ (showPrice ? ((showFamilyInOption ? ': ' : ', ') + formatCurrencyValue(item.price)) : '')
91
+ )
92
+
93
+ const Item: React.FC<{
94
+ item: LineItem
95
+ selected: boolean
96
+ }> = observer(({
97
+ item,
98
+ selected
99
+ }) => {
100
+
101
+ const textClx = (selected) ? 'text-accent' : 'text-muted'
102
+ const cursorClx = (selected) ? 'hover:cursor-default' : 'hover:cursor-pointer'
103
+ const justifyClx = (showBoth) ? '' : 'justify-center'
104
+ // If no image, the Label must fill the entire button since it has to be
105
+ // clickable all the way to the border
106
+ const paddingClx = (showImage) ? (scrollable ? 'px-4 py-2' : 'px-2 py-2' ) : ''
107
+ let bgClx = ''
108
+ let borderClx = ''
109
+ if (scrollable) {
110
+ borderClx += (selected) ? 'border-foreground border ' : 'border-b border-muted-2 '
111
+ }
112
+ else if (!showImage) {
113
+ borderClx += 'border rounded-lg '
114
+ borderClx += (selected) ? 'border-foreground ' : 'border-muted-2 '
115
+ bgClx += 'hover:bg-level-2'
116
+ }
117
+
118
+ const outerClx = ['h-10 flex items-center', justifyClx, paddingClx, bgClx, borderClx]
119
+
120
+ return (
121
+ <div className={cn(...outerClx, itemClx )} data-vaul-no-drag >
122
+ <ImageRadioGroupItem
123
+ item={item}
124
+ constrainTo={DEFAULT_CONSTRAINT}
125
+ className={cn(
126
+ cursorClx,
127
+ (scrollable ? '' : 'border-transparent border-2 data-[state=checked]:border-foreground'),
128
+ showBoth ? 'mr-2' : (showImage ? '' : 'hidden')
129
+ )}
130
+ />
131
+ <Label htmlFor={item.sku} className={cn(
132
+ showQuantity ? 'flex items-center' : 'block',
133
+ textClx,
134
+ cursorClx,
135
+ (showImage ? '' : 'self-stretch w-full flex items-center justify-center px-3 py-2'),
136
+ (showText ? '' : 'hidden')
137
+ )}>
138
+ <div className={showQuantity ? 'grow' : ''}>{labelAndPrice(item)}</div>
139
+ {showQuantity && (
140
+ <QuantityIndicator
141
+ item={item}
142
+ clx='grow-0 shrink-0 h-[20px] ml-2'
143
+ iconClx={selected ? 'fill-foreground' : 'fill-muted'}
144
+ digitClx='font-semibold text-primary-fg leading-none font-sans text-xxs'
145
+ />
146
+ )}
147
+ </Label>
148
+ </div>
149
+ )
150
+ })
151
+
152
+ return showButtonIfOnlyOne || items.length > 1 ? (
153
+ <RadioGroup
154
+ className={cn(
155
+ (scrollable ? 'shrink min-h-0 gap-0' : (mobile ? 'gap-3' : 'gap-1')),
156
+ (horizButtons ? `grid grid-cols-${items.length} gap-1` : 'flex flex-col'),
157
+ (mobile && showText) ? 'min-w-pr-50' : '',
158
+ clx,
159
+ )}
160
+ onValueChange={selectSku}
161
+ value={itemRef.item ? itemRef.item.sku : ''}
162
+ data-vaul-no-drag
163
+ >
164
+ {scrollable ? (
165
+ <ScrollArea className='mt-2 w-full h-full py-0 border border-muted-2' data-vaul-no-drag>
166
+ {items.map((item) => (
167
+ <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
168
+ ))}
169
+ </ScrollArea>
170
+ ) : (<>
171
+ {items.map((item) => (
172
+ <Item item={item} selected={itemRef.item?.sku === item.sku} key={item.sku}/>
173
+ ))}
174
+ </>)}
175
+ </RadioGroup>
176
+ ) : (
177
+ <div className={cn(showQuantity ? 'flex items-center' : 'block', soleItemClx )}>
178
+ <div className={showQuantity ? 'grow' : ''}>
179
+ {labelAndPrice(items[0])}
180
+ </div>
181
+ {showQuantity && (
182
+ <QuantityIndicator item={items[0]} clx='grow-0 shrink-0 font-semibold text-sm leading-none px-2' />
183
+ )}
184
+ </div>
185
+ )
186
+ })
187
+
188
+ export default ButtonItemSelector