@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,64 @@
1
+ import { enableStaticRendering } from 'mobx-react-lite'
2
+
3
+ import type { CommerceService, CommerceConfig } from '../../../types'
4
+ import { StandaloneService } from './index'
5
+ import { initSelectionUI } from '../../../util'
6
+
7
+ import { readSnapshot, writeSnapshotsOnChange } from './persistence'
8
+
9
+ enableStaticRendering(typeof window === "undefined")
10
+
11
+ const _LOG = false
12
+ const _log = (s: string) => {
13
+ if (!_LOG) return;
14
+ const d = new Date()
15
+ console.log(`TIMESTAMPED: ${d.getUTCMinutes()}:${d.getUTCSeconds()}:${d.getUTCMilliseconds()}`)
16
+ console.log(s)
17
+ }
18
+
19
+ // https://dev.to/ivandotv/mobx-server-side-rendering-with-next-js-4m18
20
+
21
+ let instance: StandaloneService | undefined = undefined
22
+
23
+ export const getInstance = ({
24
+ families,
25
+ rootNode,
26
+ options,
27
+ uiSpecifiers
28
+ }: CommerceConfig) : CommerceService => {
29
+
30
+ if (!options) {
31
+ throw new Error('cmmc getInstance(): Standalone Commerce Service requires config options!')
32
+ }
33
+
34
+ if (typeof window === "undefined") {
35
+ if (uiSpecifiers) {
36
+ initSelectionUI(uiSpecifiers)
37
+ }
38
+ _log("NEW INSTANCE: SERVER") //////////
39
+ return new StandaloneService(
40
+ families,
41
+ rootNode,
42
+ options
43
+ )
44
+ }
45
+
46
+ // Client side, create the store only once in the client
47
+ if (!instance) {
48
+ if (uiSpecifiers) {
49
+ initSelectionUI(uiSpecifiers)
50
+ }
51
+ _log("NEW INSTANCE: CLIENT") ///////////
52
+ const snapShot = readSnapshot(options.localStorageKey)
53
+ instance = new StandaloneService(
54
+ families,
55
+ rootNode,
56
+ options,
57
+ snapShot
58
+ )
59
+ writeSnapshotsOnChange(instance, options.localStorageKey)
60
+ }
61
+
62
+ return instance
63
+ }
64
+
@@ -0,0 +1,579 @@
1
+ import {
2
+ computed,
3
+ makeObservable,
4
+ observable,
5
+ runInAction,
6
+ action,
7
+ toJS
8
+ } from 'mobx'
9
+
10
+ import { computedFn } from 'mobx-utils'
11
+
12
+ import type {
13
+ CommerceService,
14
+ Family,
15
+ LineItem,
16
+ SelectedPaths,
17
+ CategoryNode,
18
+ CategoryNodeRole,
19
+ Promo
20
+ } from '../../../types'
21
+
22
+ import {
23
+ createOrder as createOrderHelper,
24
+ updateOrderShippingInfo as updateOrderShippingInfoHelper,
25
+ updateOrderPaymentInfo as updateOrderPaymentInfoHelper
26
+ } from './order'
27
+
28
+ import ActualLineItem, { type ActualLineItemSnapshot } from './actual-line-item'
29
+ import { getParentPath } from '../../path-utils'
30
+ import { getErrorMessage } from '../../../util'
31
+ import sep from '../../sep'
32
+
33
+ import { getInstance } from './get-instance'
34
+
35
+ type StandaloneServiceOptions = {
36
+ dbName: string
37
+ ordersTable: string
38
+ localStorageKey: string
39
+ }
40
+
41
+ interface StandaloneServiceSnapshot {
42
+ items: ActualLineItemSnapshot[]
43
+ }
44
+
45
+ class StandaloneService
46
+ implements CommerceService
47
+ {
48
+ private _familyMap = new Map<string, Family>()
49
+ private _rootNode: CategoryNode
50
+ private _selectedPaths: SelectedPaths = {}
51
+ private _promo: Promo | null = null
52
+
53
+ private _options : StandaloneServiceOptions
54
+ private _currentFamily: Family | undefined = undefined
55
+ private _currentItem: ActualLineItem | undefined = undefined
56
+
57
+ constructor(
58
+ families: Family[],
59
+ rootNode: CategoryNode,
60
+ options: StandaloneServiceOptions,
61
+ serviceSnapshot?: StandaloneServiceSnapshot,
62
+ ) {
63
+
64
+ this._rootNode = rootNode
65
+ this._options = options
66
+
67
+ families.forEach((fam) => {
68
+ fam.products = fam.products.map((p) => {
69
+ if (serviceSnapshot) {
70
+ const itemSnapshot = serviceSnapshot.items.find((is) => (is.sku === p.sku))
71
+ if (itemSnapshot) {
72
+ return new ActualLineItem(p, itemSnapshot)
73
+ }
74
+ }
75
+ return new ActualLineItem(p)
76
+ })
77
+ this._familyMap.set(fam.id, fam)
78
+ })
79
+
80
+ makeObservable<
81
+ StandaloneService,
82
+ '_selectedPaths' |
83
+ '_currentItem' |
84
+ '_currentFamily' |
85
+ '_promo' |
86
+ '_cartItems'
87
+ >(this, {
88
+ _selectedPaths : observable.deep,
89
+ _currentItem: observable.shallow,
90
+ _currentFamily: observable.shallow,
91
+ _promo: observable,
92
+ _cartItems: computed
93
+ })
94
+
95
+ makeObservable(this, {
96
+
97
+ setCurrentItem: action,
98
+ setCurrentFamily: action,
99
+ setAppliedPromo: action,
100
+ /* NOT selectPaths. It implements it's action mechanism */
101
+
102
+ cartItems: computed,
103
+ recentItem: computed,
104
+ cartQuantity: computed,
105
+ cartTotal: computed,
106
+ promoAppliedCartTotal: computed,
107
+ cartEmpty: computed,
108
+ selectedItems: computed,
109
+ selectedFamilies: computed,
110
+ hasSelection: computed,
111
+ currentItem: computed,
112
+ currentFamily: computed,
113
+ item: computed,
114
+ family: computed,
115
+ selectedPaths: computed,
116
+ appliedPromo: computed,
117
+ })
118
+ }
119
+
120
+ getFamilyById(id: string): Family | undefined {
121
+ return this._familyMap.get(id)
122
+ }
123
+
124
+ getNodeAtPath(skuPath: string): CategoryNode | undefined {
125
+ const toks = skuPath.split(sep.tok)
126
+ let level = 1
127
+ let node: CategoryNode | undefined = this._rootNode
128
+ do {
129
+ node = node!.subNodes?.find((sn) => (sn.skuToken === toks[level]))
130
+ level++
131
+ }
132
+ while (node && (level < toks.length))
133
+ return level === toks.length ? node : undefined
134
+ }
135
+
136
+ peek(skuPath: string): {
137
+ role: CategoryNodeRole
138
+ family: Family | undefined
139
+ families: Family[] | undefined
140
+ node: CategoryNode | undefined
141
+ item: LineItem | undefined
142
+ } | string /* OR error string */ {
143
+
144
+ const toks = skuPath.split(sep.tok)
145
+ let level: number
146
+ let node: CategoryNode | undefined = this._rootNode
147
+ let parent: CategoryNode | undefined = undefined
148
+
149
+ for (level = 1; level < toks.length && node && node.subNodes; level++) {
150
+ // https://stackoverflow.com/questions/62367492/inference-problem-referenced-directly-or-indirectly-in-its-own-initializer
151
+ const _node: CategoryNode | undefined =
152
+ node!.subNodes.find((sn) => (sn.skuToken === toks[level]))
153
+ if (!_node) {
154
+ return `service.peekAtNode: traversing '${skuPath}'... no CategoryNode at '${toks[level]}'!`
155
+ }
156
+ parent = node
157
+ node = _node
158
+ }
159
+
160
+ const atEnd = level === toks.length
161
+ const possibleSKU = level === toks.length - 1
162
+
163
+ let role: CategoryNodeRole = 'non-outermost'
164
+ let families: Family[] | undefined = undefined
165
+ let family: Family | undefined = undefined
166
+ let item: LineItem | undefined = undefined
167
+ let error: string | undefined = undefined
168
+
169
+ try {
170
+ if (node.subNodes && atEnd && node.outermost) {
171
+ role = 'multi-family'
172
+ families = node.subNodes.map((sub) => {
173
+ const familyId = skuPath + sep.tok + sub.skuToken
174
+ const fam = this._familyMap.get(familyId)
175
+ if (!fam) {
176
+ throw new Error(`service.peekAtNode: No Family under for CategoryNode '${skuPath}' with id ${familyId}!`)
177
+ }
178
+ return fam
179
+ })
180
+ }
181
+ else if (!node.subNodes && (atEnd || possibleSKU)) {
182
+ const _skuPath = (possibleSKU) ? getParentPath(skuPath) : skuPath
183
+ if (parent?.outermost) {
184
+ role = 'family-in-multi-family'
185
+ const fam = this._familyMap.get(_skuPath)
186
+ if (!fam) {
187
+ throw new Error(`service.peekAtNode: '${_skuPath}' graphs as a Family under a multi-family node, but no such family exists!`)
188
+ }
189
+ family = fam
190
+ const parentPath = getParentPath(_skuPath)
191
+ // get all siblings (subnodes of parent)
192
+ families = parent.subNodes!.map((sn) => {
193
+ const familyId = parentPath + sep.tok + sn.skuToken
194
+ const fam = this._familyMap.get(familyId)
195
+ if (!fam) {
196
+ throw new Error(`service.peekAtNode: No sibling Family for '${_skuPath}' with id '${familyId}'!`)
197
+ }
198
+ return fam
199
+ })
200
+ node = parent
201
+ }
202
+ else {
203
+ role = 'single-family'
204
+ const fam = this._familyMap.get(_skuPath)
205
+ if (!fam) {
206
+ throw new Error(`service.peekAtNode: '${_skuPath}' graphs as a single Family, but no such family exists!`)
207
+ }
208
+ family = fam
209
+ }
210
+ if (possibleSKU) {
211
+ const skuToTry = family.id + sep.tok + toks[toks.length - 1]
212
+ const _item = family.products.find((p) => (p.sku === skuToTry))
213
+ if (_item) {
214
+ item = _item as LineItem
215
+ }
216
+ else {
217
+ throw new Error(`service.peekAtNode: '${skuPath}' graphs as LineItem in Family '${family.id}', but no such sku exists there!`)
218
+ }
219
+ }
220
+ }
221
+ }
222
+ catch (e) {
223
+ error = getErrorMessage(e)
224
+ }
225
+
226
+ return error ?? {
227
+ role,
228
+ family,
229
+ families,
230
+ node,
231
+ item
232
+ }
233
+ }
234
+
235
+ getSelectedNodesAtLevel = computedFn((level: number): CategoryNode[] | undefined => {
236
+
237
+ let lvl = 1
238
+ let nodesAtLevel: CategoryNode[] | undefined = this._rootNode.subNodes
239
+
240
+ do {
241
+ let selectedAtLevel: CategoryNode[] | undefined = undefined
242
+ // If not specified, assume all
243
+ if (lvl in this._selectedPaths) {
244
+ selectedAtLevel = nodesAtLevel!.filter((n) => (this._selectedPaths[lvl].includes(n.skuToken)))
245
+ }
246
+ else {
247
+ selectedAtLevel = nodesAtLevel
248
+ }
249
+ let allSubsOfSelected: CategoryNode[] = []
250
+ selectedAtLevel?.forEach((n: CategoryNode) => {
251
+ if (n.subNodes) {
252
+ allSubsOfSelected = [...allSubsOfSelected, ...n.subNodes]
253
+ }
254
+ })
255
+
256
+ nodesAtLevel = allSubsOfSelected
257
+ lvl++
258
+ } while (nodesAtLevel.length > 0 && lvl <= level)
259
+
260
+ return (nodesAtLevel.length > 0 && ((lvl - 1) === level)) ? nodesAtLevel : undefined
261
+ })
262
+
263
+ get options() { return this._options}
264
+
265
+ //async createOrder(email: string, paymentMethod: string): Promise<string | undefined> {
266
+ async createOrder(email: string, name?: string): Promise<string | undefined> {
267
+ const snapshot = this.takeSnapshot()
268
+ const order = await createOrderHelper(email, snapshot.items, this._options, name) // didn't want to have two levels of 'items'
269
+ return order.id
270
+ }
271
+
272
+ // TODO: add shippingInfo type
273
+ async updateOrderShippingInfo(orderId: string, shippingInfo: any): Promise<void> {
274
+ updateOrderShippingInfoHelper(orderId, shippingInfo, this._options)
275
+ }
276
+
277
+ // TODO: add paymentInfo type
278
+ async updateOrderPaymentInfo(orderId: string, paymentInfo: any): Promise<void> {
279
+ updateOrderPaymentInfoHelper(orderId, paymentInfo, this._options)
280
+ }
281
+ // Might as well use the ordered set.
282
+ takeSnapshot = (): StandaloneServiceSnapshot => ({
283
+ items : (this.cartItems as ActualLineItem[]).map((it) => (it.takeSnapshot(this)))
284
+ })
285
+
286
+ // last cartItem whose quantity was modified (undefined if cartEmpty)
287
+ get recentItem(): { item: LineItem, modified: number} | undefined {
288
+
289
+ if (this.cartEmpty) return undefined;
290
+
291
+ const mostRecent = this._cartItems.reduce(
292
+ (newest, item) => ((!newest) ? item : (
293
+ (item as ActualLineItem).timeModified > (newest as ActualLineItem).timeModified) ? item : newest
294
+ ),
295
+ undefined as LineItem | undefined
296
+ )
297
+ return { item: mostRecent!, modified: (mostRecent! as ActualLineItem).timeModified }
298
+ }
299
+
300
+ private get _cartItems(): LineItem[] {
301
+ let result: LineItem[] = []
302
+ this._familyMap.forEach((fam) => {
303
+ result = [...result, ...(fam.products as LineItem[]).filter((item) => (item.isInCart))]
304
+ })
305
+ return result
306
+ }
307
+
308
+ get cartItems(): LineItem[] {
309
+ return this._cartItems.sort((it1, it2) => ((it1 as ActualLineItem).timeAdded - (it2 as ActualLineItem).timeAdded))
310
+ }
311
+
312
+
313
+
314
+ get cartEmpty(): boolean {
315
+ return this._cartItems.length === 0
316
+ }
317
+
318
+ get cartTotal(): number {
319
+ return this._cartItems.reduce(
320
+ (total, item) => (total + item.price * item.quantity),
321
+ 0
322
+ )
323
+ }
324
+
325
+ _promoValue_unsafe(value: number): number {
326
+ if (this._promo!.type === 'percent') {
327
+ return value * (1 - this._promo!.value / 100)
328
+ }
329
+ return value - this._promo!.value
330
+ }
331
+
332
+ get promoAppliedCartTotal(): number {
333
+ if (!this._promo) {
334
+ return this.cartTotal
335
+ }
336
+ if (!this._promo.skus) {
337
+ return this._promoValue_unsafe(this.cartTotal)
338
+ }
339
+ let total = this._cartItems.reduce(
340
+ (total, item) => {
341
+ const itemPrice = this._promo!.skus!.includes(item.sku) ?
342
+ this._promoValue_unsafe(item.price)
343
+ :
344
+ item.price
345
+ return total + itemPrice * item.quantity
346
+ },
347
+ 0
348
+ )
349
+ return total
350
+ }
351
+
352
+ itemPromoPrice(item: LineItem): number | undefined {
353
+ if (this._promo && (!this._promo.skus || this._promo.skus.includes(item.sku) )) {
354
+ return this._promoValue_unsafe(item.price)
355
+ }
356
+ return undefined
357
+ }
358
+
359
+ get cartQuantity(): number {
360
+ return this.cartItems.reduce(
361
+ (total, item) => (total + item.quantity),
362
+ 0
363
+ )
364
+ }
365
+
366
+ get appliedPromo(): Promo | null {
367
+ return this._promo
368
+ }
369
+
370
+ setAppliedPromo(promo: Promo | null): void {
371
+ this._promo = promo
372
+ }
373
+
374
+ getItemBySku = (skuToFind: string | undefined): LineItem | undefined => {
375
+
376
+ if (skuToFind === undefined || skuToFind.length === 0) {
377
+ return undefined
378
+ }
379
+ // Self-calling
380
+ const found = ((): ActualLineItem | undefined => {
381
+
382
+ const familiesTried: string[] = []
383
+ if (this.selectedFamilies && this.selectedFamilies.length > 0) {
384
+ for (let family of this.selectedFamilies) {
385
+ familiesTried.push(family.id)
386
+ const foundItem = family.products.find((p) => (p.sku === skuToFind))
387
+ if (foundItem) {
388
+ return foundItem as ActualLineItem
389
+ }
390
+ }
391
+ }
392
+ for( const [familyId, family] of this._familyMap.entries()) {
393
+ if (familiesTried.includes(familyId)) continue
394
+ const foundItem = family.products.find((p) => (p.sku === skuToFind)) as ActualLineItem | undefined
395
+ if (foundItem) {
396
+ return foundItem as ActualLineItem
397
+ }
398
+ }
399
+ return undefined
400
+ })(); // Self-calling, necessary semi
401
+
402
+ return found
403
+ }
404
+
405
+ setCurrentItem = (skuToFind: string | undefined): boolean => {
406
+
407
+ if (skuToFind === undefined || skuToFind.length === 0) {
408
+ this._currentItem = undefined
409
+ return true
410
+ }
411
+ // self calling function
412
+ this._currentItem = this.getItemBySku(skuToFind) as ActualLineItem | undefined
413
+ this.setCurrentFamily(this._currentItem ? this._currentItem.familyId : undefined)
414
+ return !!this._currentItem
415
+ }
416
+
417
+ /* for ObsLineItemRef */
418
+ get item(): LineItem | undefined {
419
+ return this._currentItem
420
+ }
421
+
422
+ get currentItem(): LineItem | undefined {
423
+ return this._currentItem
424
+ }
425
+
426
+ setCurrentFamily(id: string | undefined): boolean {
427
+
428
+ if (id === undefined || id.length === 0) {
429
+ this._currentFamily = undefined
430
+ return true
431
+ }
432
+
433
+ const fam = this._familyMap.get(id)
434
+ this._currentFamily = fam // undef ok
435
+
436
+ if (
437
+ this._currentFamily &&
438
+ this._currentItem &&
439
+ this._currentItem.familyId !== this._currentFamily.id
440
+ ) {
441
+ this._currentItem = undefined
442
+ }
443
+
444
+ return !!this._currentFamily
445
+ }
446
+
447
+ get currentFamily(): Family | undefined {
448
+ return this._currentFamily
449
+ }
450
+
451
+ /* for ObsFamilyRef */
452
+ get family(): Family | undefined {
453
+ return this._currentFamily
454
+ }
455
+
456
+ selectPaths(sel: SelectedPaths): Family[] {
457
+ runInAction (() => {
458
+ this._selectedPaths = this._processAndValidate(sel)
459
+ })
460
+ return this.selectedFamilies
461
+ }
462
+
463
+ selectPath(skuPath: string): Family[] {
464
+ const toks = skuPath.split(sep.tok)
465
+ const highestLevel = toks.length - 1
466
+ const fsv: SelectedPaths = {}
467
+ for (let level = 1; level <= highestLevel; level++ ) {
468
+ fsv[level] = [toks[level]]
469
+ }
470
+ return this.selectPaths(fsv)
471
+ }
472
+
473
+ get selectedPaths(): SelectedPaths {
474
+ const result: SelectedPaths = {}
475
+ for( let level in this._selectedPaths ) {
476
+ result[level] = [...this._selectedPaths[level]]
477
+ }
478
+ return result
479
+ }
480
+
481
+ get selectedFamilies(): Family[] {
482
+ if (Object.keys(toJS(this._selectedPaths)).length === 0) {
483
+ // FacetsDesc have never been set or unset, so cannot evaluate them
484
+ return []
485
+ }
486
+
487
+ return this._rootNode.subNodes!.reduce(
488
+ (acc: Family[], subFacet: CategoryNode) => (
489
+ // Pass the root token as a one member array
490
+ this._reduceNode([this._rootNode.skuToken], acc, subFacet)
491
+ ),
492
+ []
493
+ )
494
+ }
495
+
496
+ private _reduceNode(parentPath: string[], acc: Family[], node: CategoryNode): Family[] {
497
+ const path = [...parentPath, node.skuToken] // Don't mutate original please :)
498
+ const level = path.length - 1
499
+ // If there is no token array supplied for this level,
500
+ // assume all are specified. Otherwise, see if the
501
+ // current node is in the array
502
+ const specified = (
503
+ !this._selectedPaths[level]
504
+ ||
505
+ this._selectedPaths[level].includes(node.skuToken)
506
+ )
507
+ if (specified) {
508
+ // Process subnodes
509
+ if (node.subNodes && node.subNodes.length > 0) {
510
+ return node.subNodes.reduce((acc, n) => (
511
+ this._reduceNode(path, acc, n)
512
+ )
513
+ , acc)
514
+ }
515
+ // Process leaf
516
+ const fam = this._familyMap.get(path.join(sep.tok))
517
+ if (!fam) {
518
+ throw new Error("selectedFamilies WTF?!" + path.join(sep.tok))
519
+ }
520
+ acc.push(fam)
521
+ }
522
+ return acc
523
+ }
524
+
525
+ private _processAndValidate(partial: SelectedPaths): SelectedPaths {
526
+
527
+ const result: SelectedPaths = {}
528
+
529
+ let level = 1
530
+ let currentSet = this._rootNode.subNodes!
531
+
532
+ while (true) {
533
+ let possibleCurrent = currentSet.map((el) => (el.skuToken))
534
+ const validTokens = !partial[level] ? undefined : partial[level].filter((tok) => possibleCurrent.includes(tok))
535
+ if (!validTokens) {
536
+ break
537
+ }
538
+ result[level] = validTokens
539
+ currentSet = validTokens.map((tok) => {
540
+ const fd = currentSet.find((node) => ( node.skuToken === tok ))
541
+ return (fd && fd.subNodes && fd.subNodes.length > 0) ? fd.subNodes : []
542
+ }).flat()
543
+ level++
544
+ }
545
+
546
+ return result
547
+ }
548
+
549
+ get selectedItems(): LineItem[] {
550
+ if (Object.keys(toJS(this._selectedPaths)).length === 0) {
551
+ // FacetsDesc have never been set or unset, so cannot evaluate them
552
+ return []
553
+ }
554
+
555
+ return this.selectedFamilies.reduce(
556
+ (allProducts, fam) => ([...allProducts, ...(fam.products as LineItem[])]), [] as LineItem[])
557
+ }
558
+
559
+ get hasSelection(): boolean {
560
+ return this.selectedFamilies.length > 0
561
+ }
562
+
563
+ getFamilySubtotal(familyId: string): number {
564
+ const c = this._familyMap.get(familyId)!
565
+ return (c.products as LineItem[]).reduce(
566
+ // avoid floating point bs around zero
567
+ (total, item) => (item.quantity > 0 ? total + item.price * item.quantity : total),
568
+ 0
569
+ )
570
+ }
571
+
572
+ }
573
+
574
+ export {
575
+ type StandaloneServiceOptions,
576
+ type StandaloneServiceSnapshot,
577
+ StandaloneService,
578
+ getInstance
579
+ }
@@ -0,0 +1,14 @@
1
+ import { initializeApp, getApps } from 'firebase/app'
2
+
3
+ const firebaseConfig = {
4
+ apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
5
+ authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
6
+ projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
7
+ storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
8
+ messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
9
+ appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
10
+ measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
11
+ }
12
+
13
+ // Initialize Firebase instance if there isn't one already
14
+ export default getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0]