ordering-components-external 13.2.13 → 13.2.14

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 (554) hide show
  1. package/.babelrc +22 -22
  2. package/.vscode/settings.json +3 -3
  3. package/_bundles/0.ordering-component.71fcc1b93f7c954870c3.js +1 -0
  4. package/_bundles/{1.ordering-component.aa21761da1477298dd80.js → 1.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  5. package/_bundles/{2.ordering-component.aa21761da1477298dd80.js → 2.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  6. package/_bundles/{4.ordering-component.aa21761da1477298dd80.js → 4.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  7. package/_bundles/{5.ordering-component.aa21761da1477298dd80.js → 5.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  8. package/_bundles/{6.ordering-component.aa21761da1477298dd80.js → 6.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  9. package/_bundles/7.ordering-component.71fcc1b93f7c954870c3.js +2 -0
  10. package/_bundles/7.ordering-component.71fcc1b93f7c954870c3.js.LICENSE.txt +1 -0
  11. package/_bundles/{8.ordering-component.aa21761da1477298dd80.js → 8.ordering-component.71fcc1b93f7c954870c3.js} +1 -1
  12. package/_bundles/ordering-component.71fcc1b93f7c954870c3.js +2 -0
  13. package/_bundles/{ordering-component.aa21761da1477298dd80.js.LICENSE.txt → ordering-component.71fcc1b93f7c954870c3.js.LICENSE.txt} +0 -9
  14. package/_modules/components/AddressDetails/index.js +35 -35
  15. package/_modules/components/AddressForm/index.js +63 -63
  16. package/_modules/components/AddressList/index.js +56 -56
  17. package/_modules/components/Analitycs/index.js +11 -11
  18. package/_modules/components/AnalyticsSegment/index.js +11 -11
  19. package/_modules/components/AppleLogin/index.js +37 -37
  20. package/_modules/components/BaseComponent/index.js +15 -15
  21. package/_modules/components/BusinessAndProductList/index.js +43 -43
  22. package/_modules/components/BusinessBasicInformation/index.js +30 -30
  23. package/_modules/components/BusinessController/index.js +65 -65
  24. package/_modules/components/BusinessInformation/BusinessOption/index.js +22 -22
  25. package/_modules/components/BusinessInformation/index.js +24 -24
  26. package/_modules/components/BusinessList/index.js +57 -57
  27. package/_modules/components/BusinessMenuListing/index.js +30 -30
  28. package/_modules/components/BusinessProductsCategories/index.js +18 -18
  29. package/_modules/components/BusinessProductsSearch/index.js +16 -16
  30. package/_modules/components/BusinessReviews/index.js +37 -37
  31. package/_modules/components/BusinessSearchList/index.js +34 -34
  32. package/_modules/components/BusinessSortControl/index.js +34 -34
  33. package/_modules/components/BusinessTypeFilter/index.js +36 -36
  34. package/_modules/components/BusinessesMap/index.js +33 -33
  35. package/_modules/components/Cart/index.js +48 -48
  36. package/_modules/components/CartStoresListing/index.js +15 -15
  37. package/_modules/components/Checkout/index.js +83 -83
  38. package/_modules/components/CmsContent/index.js +30 -30
  39. package/_modules/components/Contacts/index.js +45 -45
  40. package/_modules/components/CouponControl/index.js +29 -29
  41. package/_modules/components/DragAndDrop/index.js +4 -4
  42. package/_modules/components/DriverList/index.js +31 -31
  43. package/_modules/components/DriverTips/index.js +41 -41
  44. package/_modules/components/Emitter/index.js +3 -3
  45. package/_modules/components/FacebookLoginButton/index.js +50 -50
  46. package/_modules/components/FacebookPixel/index.js +8 -8
  47. package/_modules/components/FavoriteList/index.js +43 -43
  48. package/_modules/components/FirebaseGoogleLoginButton/index.js +21 -21
  49. package/_modules/components/FloatingButton/index.js +22 -22
  50. package/_modules/components/ForgotPasswordForm/index.js +53 -53
  51. package/_modules/components/GiftCard/GiftCardOrdersList/index.js +16 -16
  52. package/_modules/components/GiftCard/PurchaseGiftCard/index.js +17 -17
  53. package/_modules/components/GiftCard/RedeemGiftCard/index.js +14 -14
  54. package/_modules/components/GiftCard/SendGiftCard/index.js +14 -14
  55. package/_modules/components/GoogleAutocompleteInput/index.js +25 -25
  56. package/_modules/components/GoogleIdentity/index.js +38 -38
  57. package/_modules/components/GoogleLoginButton/index.js +61 -61
  58. package/_modules/components/GoogleMaps/index.js +44 -44
  59. package/_modules/components/GpsButton/index.js +27 -27
  60. package/_modules/components/LanguageSelector/index.js +38 -38
  61. package/_modules/components/LoginForm/index.js +72 -72
  62. package/_modules/components/LogoutAction/index.js +28 -28
  63. package/_modules/components/MainSearch/index.js +41 -41
  64. package/_modules/components/MapView/index.js +12 -12
  65. package/_modules/components/MenuControl/index.js +58 -58
  66. package/_modules/components/Messages/index.js +35 -35
  67. package/_modules/components/MomentOption/index.js +57 -57
  68. package/_modules/components/MultiCartCreate/index.js +10 -10
  69. package/_modules/components/MultiCartsPaymethodsAndWallets/index.js +22 -22
  70. package/_modules/components/MultiCheckout/index.js +34 -34
  71. package/_modules/components/MultiOrdersDetails/index.js +19 -19
  72. package/_modules/components/MyOrders/index.js +35 -35
  73. package/_modules/components/MyOrdersList/index.js +35 -35
  74. package/_modules/components/NewOrderNotification/index.js +8 -8
  75. package/_modules/components/OrderChange/index.js +30 -30
  76. package/_modules/components/OrderDetails/index.js +66 -66
  77. package/_modules/components/OrderList/index.js +67 -67
  78. package/_modules/components/OrderListGroups/index.js +28 -28
  79. package/_modules/components/OrderReview/index.js +35 -35
  80. package/_modules/components/OrderTypeControl/index.js +23 -23
  81. package/_modules/components/OrderVerticalList/index.js +15 -15
  82. package/_modules/components/OrdersControlFilters/index.js +15 -15
  83. package/_modules/components/OrdersDashboardComponents/Appointments/index.js +14 -14
  84. package/_modules/components/OrdersDashboardComponents/BusinessProductsListing/index.js +51 -51
  85. package/_modules/components/OrdersDashboardComponents/CheckPassword/index.js +35 -35
  86. package/_modules/components/OrdersDashboardComponents/CityList/index.js +33 -33
  87. package/_modules/components/OrdersDashboardComponents/CountryList/index.js +32 -32
  88. package/_modules/components/OrdersDashboardComponents/CustomOrderDetails/index.js +25 -25
  89. package/_modules/components/OrdersDashboardComponents/DashboardBusinessList/index.js +75 -75
  90. package/_modules/components/OrdersDashboardComponents/DashboardOrdersList/index.js +77 -77
  91. package/_modules/components/OrdersDashboardComponents/DriversList/index.js +73 -73
  92. package/_modules/components/OrdersDashboardComponents/ExportCSV/index.js +26 -26
  93. package/_modules/components/OrdersDashboardComponents/GiftCardsList/index.js +16 -16
  94. package/_modules/components/OrdersDashboardComponents/GoogleMapsApiKeySetting/index.js +14 -14
  95. package/_modules/components/OrdersDashboardComponents/LogisticInformation/index.js +29 -29
  96. package/_modules/components/OrdersDashboardComponents/Logistics/index.js +29 -29
  97. package/_modules/components/OrdersDashboardComponents/Messages/index.js +42 -42
  98. package/_modules/components/OrdersDashboardComponents/MetaFields/index.js +40 -40
  99. package/_modules/components/OrdersDashboardComponents/OrderDetails/index.js +54 -54
  100. package/_modules/components/OrdersDashboardComponents/OrderNotification/index.js +19 -19
  101. package/_modules/components/OrdersDashboardComponents/OrdersFilter/index.js +73 -73
  102. package/_modules/components/OrdersDashboardComponents/OrdersManage/index.js +74 -74
  103. package/_modules/components/OrdersDashboardComponents/PointsWalletLevels/index.js +38 -38
  104. package/_modules/components/OrdersDashboardComponents/ReviewCustomer/index.js +26 -26
  105. package/_modules/components/OrdersDashboardComponents/Schedule/index.js +56 -56
  106. package/_modules/components/OrdersDashboardComponents/SettingsList/index.js +51 -51
  107. package/_modules/components/OrdersDashboardComponents/UserFormDetails/index.js +82 -82
  108. package/_modules/components/OrdersDashboardComponents/UsersList/index.js +72 -72
  109. package/_modules/components/OrdersDashboardComponents/WebsocketStatus/index.js +10 -10
  110. package/_modules/components/PageBanner/index.js +17 -17
  111. package/_modules/components/PaymentOptionCash/index.js +26 -26
  112. package/_modules/components/PaymentOptionPaypal/index.js +35 -35
  113. package/_modules/components/PaymentOptionSquare/index.js +22 -22
  114. package/_modules/components/PaymentOptionStripe/index.js +45 -45
  115. package/_modules/components/PaymentOptionStripeDirect/index.js +34 -34
  116. package/_modules/components/PaymentOptionStripeRedirect/StripeRedirectForm/index.js +24 -24
  117. package/_modules/components/PaymentOptionStripeRedirect/index.js +39 -39
  118. package/_modules/components/PaymentOptionWallet/index.js +11 -11
  119. package/_modules/components/PaymentOptions/index.js +37 -37
  120. package/_modules/components/PaymethodList/index.js +30 -30
  121. package/_modules/components/PhoneAutocomplete/index.js +30 -30
  122. package/_modules/components/PlaceSpot/index.js +15 -15
  123. package/_modules/components/Popup/index.js +39 -39
  124. package/_modules/components/ProductComponent/index.js +36 -36
  125. package/_modules/components/ProductForm/index.js +145 -145
  126. package/_modules/components/ProductImages/index.js +20 -20
  127. package/_modules/components/ProductIngredient/index.js +19 -19
  128. package/_modules/components/ProductItemAccordion/index.js +18 -18
  129. package/_modules/components/ProductOption/index.js +9 -9
  130. package/_modules/components/ProductOptionSuboption/index.js +36 -36
  131. package/_modules/components/ProductShare/index.js +26 -26
  132. package/_modules/components/ProductsList/index.js +26 -26
  133. package/_modules/components/ProductsListing/index.js +50 -50
  134. package/_modules/components/ProfessionalInfo/index.js +34 -34
  135. package/_modules/components/PromotionsController/index.js +10 -10
  136. package/_modules/components/QueryLoginSpoonity/index.js +30 -30
  137. package/_modules/components/ReCaptcha/index.js +9 -9
  138. package/_modules/components/ResetPassword/index.js +30 -30
  139. package/_modules/components/ReviewCustomer/index.js +26 -26
  140. package/_modules/components/ReviewDriver/index.js +33 -33
  141. package/_modules/components/ReviewProduct/index.js +38 -38
  142. package/_modules/components/SearchOptions/index.js +23 -23
  143. package/_modules/components/Sessions/index.js +33 -33
  144. package/_modules/components/SignupForm/index.js +71 -71
  145. package/_modules/components/SingleBusinessCard/index.js +29 -29
  146. package/_modules/components/SingleOrderCard/index.js +32 -32
  147. package/_modules/components/SingleProductCard/index.js +28 -28
  148. package/_modules/components/SingleProfessionalCard/index.js +28 -28
  149. package/_modules/components/SmartAppBanner/index.js +13 -13
  150. package/_modules/components/StoreProductList/index.js +46 -46
  151. package/_modules/components/StripeElementsForm/CardForm/index.js +36 -36
  152. package/_modules/components/StripeElementsForm/index.js +25 -25
  153. package/_modules/components/UpsellingPage/index.js +27 -27
  154. package/_modules/components/UserFormDetails/index.js +85 -85
  155. package/_modules/components/UserVerification/index.js +28 -28
  156. package/_modules/components/WalletList/index.js +11 -11
  157. package/_modules/components/WebsocketStatus/index.js +10 -10
  158. package/_modules/components/WrapperGoogleMaps/index.js +9 -9
  159. package/_modules/contexts/ApiContext/index.js +13 -13
  160. package/_modules/contexts/BillingContext/index.js +7 -7
  161. package/_modules/contexts/BusinessContext/index.js +18 -18
  162. package/_modules/contexts/ConfigContext/index.js +19 -19
  163. package/_modules/contexts/CustomerContext/index.js +10 -10
  164. package/_modules/contexts/EventContext/index.js +9 -9
  165. package/_modules/contexts/LanguageContext/index.js +28 -28
  166. package/_modules/contexts/OptimizationLoadContext/index.js +16 -16
  167. package/_modules/contexts/OrderContext/index.js +97 -97
  168. package/_modules/contexts/OrderingContext/index.js +7 -7
  169. package/_modules/contexts/OrderingThemeContext/index.js +19 -19
  170. package/_modules/contexts/ProductContext/index.js +14 -14
  171. package/_modules/contexts/SessionContext/index.js +23 -23
  172. package/_modules/contexts/SiteContext/index.js +18 -18
  173. package/_modules/contexts/ToastContext/index.js +4 -4
  174. package/_modules/contexts/UtilsContext/index.js +14 -14
  175. package/_modules/contexts/ValidationsFieldsContext/index.js +9 -9
  176. package/_modules/contexts/WebsocketContext/index.js +17 -17
  177. package/_modules/contexts/WebsocketContext/socket.js +4 -4
  178. package/_modules/native/src/NativeStrategy/index.js +9 -9
  179. package/_modules/native/src/contexts/OrderingContext/index.js +7 -7
  180. package/_modules/utils/index.js +2 -2
  181. package/_modules/webStrategy/index.js +8 -8
  182. package/cypress/fixtures/example.json +4 -4
  183. package/cypress/integration/naked/BusinessProductsCategories.spec.js +9 -9
  184. package/cypress/integration/naked/PhoneAutocomplete.spec.js +22 -22
  185. package/cypress/integration/naked/activeOrders.spec.js +15 -15
  186. package/cypress/integration/naked/addressDetails.spec.js +10 -10
  187. package/cypress/integration/naked/appleLogin.spec.js +14 -14
  188. package/cypress/integration/naked/businessBasicInformation.spec.js +14 -14
  189. package/cypress/integration/naked/businessController.spec.js +9 -9
  190. package/cypress/integration/naked/businessInformation.spec.js +19 -19
  191. package/cypress/integration/naked/businessProductsSearch.spec.js +9 -9
  192. package/cypress/integration/naked/businessReviews.spec.js +16 -16
  193. package/cypress/integration/naked/businessSortControl.spec.js +9 -9
  194. package/cypress/integration/naked/businessTypeFilter.spec.js +10 -10
  195. package/cypress/integration/naked/businessesMap.spec.js +13 -13
  196. package/cypress/integration/naked/cms.spec.js +9 -9
  197. package/cypress/integration/naked/config.spec.js +34 -34
  198. package/cypress/integration/naked/driverTips.spec.js +10 -10
  199. package/cypress/integration/naked/events.spec.js +13 -13
  200. package/cypress/integration/naked/facebookLogin.spec.js +13 -13
  201. package/cypress/integration/naked/floatingButton.spec.js +13 -13
  202. package/cypress/integration/naked/forgotPassword.spec.js +20 -20
  203. package/cypress/integration/naked/googleLogin.spec.js +11 -11
  204. package/cypress/integration/naked/languageExamples.spec.js +25 -25
  205. package/cypress/integration/naked/languageSelector.spec.js +10 -10
  206. package/cypress/integration/naked/login.spec.js +38 -38
  207. package/cypress/integration/naked/logout.spec.js +15 -15
  208. package/cypress/integration/naked/mainSearch.spec.js +9 -9
  209. package/cypress/integration/naked/menuControl.spec.js +9 -9
  210. package/cypress/integration/naked/messages.spec.js +25 -25
  211. package/cypress/integration/naked/momentOption.spec.js +10 -10
  212. package/cypress/integration/naked/myOrders.spec.js +11 -11
  213. package/cypress/integration/naked/myOrdersList.spec.js +9 -9
  214. package/cypress/integration/naked/orderContextAdvanced.spec.js +23 -23
  215. package/cypress/integration/naked/orderDetails.spec.js +11 -11
  216. package/cypress/integration/naked/paymentOptionCash.spec.js +21 -21
  217. package/cypress/integration/naked/paymentOptionStripe.spec.js +11 -11
  218. package/cypress/integration/naked/paymentOptionStripeDirect.spec.js +11 -11
  219. package/cypress/integration/naked/paymentOptions.spec.js +9 -9
  220. package/cypress/integration/naked/popupExample.spec.js +17 -17
  221. package/cypress/integration/naked/productImages.spec.js +11 -11
  222. package/cypress/integration/naked/productOptionExample.spec.js +21 -21
  223. package/cypress/integration/naked/productShare.spec.js +9 -9
  224. package/cypress/integration/naked/productsList.spec.js +9 -9
  225. package/cypress/integration/naked/resetPassword.spec.js +11 -11
  226. package/cypress/integration/naked/reviewOrders.spec.js +13 -13
  227. package/cypress/integration/naked/searchOptions.spec.js +18 -18
  228. package/cypress/integration/naked/signup.spec.js +31 -31
  229. package/cypress/integration/naked/upselling.spec.js +13 -13
  230. package/cypress/integration/naked/userDetails.spec.js +12 -12
  231. package/cypress/plugins/index.js +21 -21
  232. package/cypress/support/commands.js +35 -35
  233. package/cypress/support/index.js +20 -20
  234. package/cypress.json +12 -12
  235. package/example/App.js +263 -263
  236. package/example/components/ActiveOrdersUI/index.js +72 -72
  237. package/example/components/AddressDetailsUI/index.js +101 -101
  238. package/example/components/AddressFormUI/index.js +161 -161
  239. package/example/components/AddressListUI/index.js +33 -33
  240. package/example/components/AlertPopup/index.js +10 -10
  241. package/example/components/AlertUI/index.js +49 -49
  242. package/example/components/AlertUI/style.css +5 -5
  243. package/example/components/AppleLoginUI/index.js +40 -40
  244. package/example/components/BaseComponentUI/index.js +34 -34
  245. package/example/components/BusinessBasicInformationUI/index.js +118 -118
  246. package/example/components/BusinessControllerUI/index.js +89 -89
  247. package/example/components/BusinessInformationUI/BusinessOptionUI/index.js +83 -83
  248. package/example/components/BusinessInformationUI/index.js +83 -83
  249. package/example/components/BusinessProductsCategoriesUI/index.js +42 -42
  250. package/example/components/BusinessProductsSearchUI/index.js +38 -38
  251. package/example/components/BusinessReviewsUI/index.js +77 -77
  252. package/example/components/BusinessSortControlUI/index.js +47 -47
  253. package/example/components/BusinessTypeFilterUI/index.js +53 -53
  254. package/example/components/BusinessesMapUI/index.js +57 -57
  255. package/example/components/CartUI/index.js +154 -154
  256. package/example/components/ChangeView/index.js +19 -19
  257. package/example/components/CheckoutUI/index.js +206 -206
  258. package/example/components/CmsContentUI/index.js +52 -52
  259. package/example/components/ConfigsExample/index.js +118 -118
  260. package/example/components/CouponControlUI/index.js +63 -63
  261. package/example/components/DriverTipsUI/index.js +58 -58
  262. package/example/components/FacebookLoginButtonUI/index.js +48 -48
  263. package/example/components/FloatingButtonUI/index.js +145 -145
  264. package/example/components/ForgotPasswordFormUI/index.js +93 -93
  265. package/example/components/GoogleLoginUI/index.js +30 -30
  266. package/example/components/GpsButtonUI/index.js +22 -22
  267. package/example/components/Header/index.js +18 -18
  268. package/example/components/LanguageSelectorUI/index.js +57 -57
  269. package/example/components/LanguagesExample/index.js +51 -51
  270. package/example/components/LoginFormUI/index.js +159 -159
  271. package/example/components/LogoutButtonUI/index.js +21 -21
  272. package/example/components/MainSearchUI/index.js +131 -131
  273. package/example/components/MenuControlUI/index.js +103 -103
  274. package/example/components/MessagesUI/index.js +162 -162
  275. package/example/components/ModalUI/index.js +36 -36
  276. package/example/components/ModalUI/style.css +5 -5
  277. package/example/components/MomentOptionUI/index.js +68 -68
  278. package/example/components/MyOrdersListUI/index.js +51 -51
  279. package/example/components/MyOrdersUI/index.js +52 -52
  280. package/example/components/OrderChangeUI/index.js +54 -54
  281. package/example/components/OrderDetailsUI/index.js +174 -174
  282. package/example/components/OrderReviewUI/index.js +125 -125
  283. package/example/components/OrderTypeControlUI/index.js +32 -32
  284. package/example/components/PaymentOptionCashUI/index.js +60 -60
  285. package/example/components/PaymentOptionPaypalUI/index.js +50 -50
  286. package/example/components/PaymentOptionStripeDirectUI/index.js +89 -89
  287. package/example/components/PaymentOptionStripeRedirectUI/index.js +97 -97
  288. package/example/components/PaymentOptionStripeUI/index.js +129 -129
  289. package/example/components/PaymentOptionsUI/index.js +169 -169
  290. package/example/components/PhoneAutocompleteUI/index.js +67 -67
  291. package/example/components/PhoneAutocompleteUI/styles.css +49 -49
  292. package/example/components/ProductComponentUI/index.js +113 -113
  293. package/example/components/ProductFormUI/index.js +131 -131
  294. package/example/components/ProductImagesUI/index.js +82 -82
  295. package/example/components/ProductIngredientUI/index.js +21 -21
  296. package/example/components/ProductOptionSuboptionUI/index.js +65 -65
  297. package/example/components/ProductOptionUI/index.js +31 -31
  298. package/example/components/ProductShareUI/index.js +48 -48
  299. package/example/components/ProductsListUI/index.js +108 -108
  300. package/example/components/ProductsListingUI/index.js +42 -42
  301. package/example/components/ResetPasswordUI/index.js +121 -121
  302. package/example/components/SearchOptionsUI/index.js +82 -82
  303. package/example/components/SignupFormUI/index.js +117 -117
  304. package/example/components/SingleBusinessCardUI/index.js +82 -82
  305. package/example/components/SingleOrderCardUI/index.js +52 -52
  306. package/example/components/SingleProductCardUI/index.js +47 -47
  307. package/example/components/StripeElementsFormUI/CardFormUI/index.js +51 -51
  308. package/example/components/StripeElementsFormUI/CardFormUI/style.css +118 -118
  309. package/example/components/StripeElementsFormUI/index.js +38 -38
  310. package/example/components/StripeRedirectFormUI/index.js +99 -99
  311. package/example/components/TestComponent/index.js +5 -5
  312. package/example/components/UpsellingPageUI/index.js +26 -26
  313. package/example/components/UserDetailsUI/index.js +94 -94
  314. package/example/components/UserProfileUI/index.js +156 -156
  315. package/example/views/ActiveOrders/index.js +86 -86
  316. package/example/views/AddressDetailsExample/index.js +57 -57
  317. package/example/views/AppleLoginExample/index.js +51 -51
  318. package/example/views/BaseComponentExample/index.js +35 -35
  319. package/example/views/BusinessBasicInformationExample/index.js +43 -43
  320. package/example/views/BusinessControllerExample/index.js +55 -55
  321. package/example/views/BusinessInformationExample/index.js +68 -68
  322. package/example/views/BusinessProductsCategoriesExample/index.js +50 -50
  323. package/example/views/BusinessProductsSearchExample/index.js +39 -39
  324. package/example/views/BusinessReviewsExample/index.js +43 -43
  325. package/example/views/BusinessSortControlExample/index.js +53 -53
  326. package/example/views/BusinessTypeFilterExample/index.js +53 -53
  327. package/example/views/BusinessesMapExample/index.js +57 -57
  328. package/example/views/CheckoutExample/index.js +143 -143
  329. package/example/views/CmsContentExample/index.js +44 -44
  330. package/example/views/DriverTipsExample/index.js +47 -47
  331. package/example/views/EventsExample/index.js +26 -26
  332. package/example/views/FacebookLogin/index.js +63 -63
  333. package/example/views/FloatingButtonExample/index.js +47 -47
  334. package/example/views/ForgotPassword/index.js +77 -77
  335. package/example/views/GoogleLoginExample/index.js +85 -85
  336. package/example/views/Home/index.js +200 -200
  337. package/example/views/LanguageSelectorExample/index.js +55 -55
  338. package/example/views/Login/index.js +84 -84
  339. package/example/views/MainSearchExample/index.js +43 -43
  340. package/example/views/MenuControlExample/index.js +68 -68
  341. package/example/views/MessagesExample/index.js +58 -58
  342. package/example/views/MomentOptionExample/index.js +52 -52
  343. package/example/views/MyOrdersExample/index.js +35 -35
  344. package/example/views/MyOrdersListExample/index.js +50 -50
  345. package/example/views/OrderChangeExample/index.js +46 -46
  346. package/example/views/OrderContextExample/index.js +139 -139
  347. package/example/views/OrderDetailsExample/index.js +50 -50
  348. package/example/views/OrderReviewExample/index.js +64 -64
  349. package/example/views/PaymentOptionCashExample/index.js +51 -51
  350. package/example/views/PaymentOptionPaypalExample/index.js +71 -71
  351. package/example/views/PaymentOptionStripeDirectExample/index.js +43 -43
  352. package/example/views/PaymentOptionStripeExample/index.js +47 -47
  353. package/example/views/PaymentOptionStripeRedirectExample/index.js +56 -56
  354. package/example/views/PaymentOptionsExample/index.js +47 -47
  355. package/example/views/PhoneAutocompleteExample/index.js +34 -34
  356. package/example/views/PlacesExample/index.js +94 -94
  357. package/example/views/PopupExample/index.js +78 -78
  358. package/example/views/PopupExample/style.css +18 -18
  359. package/example/views/ProductDetail/index.js +323 -323
  360. package/example/views/ProductImagesExample/index.js +43 -43
  361. package/example/views/ProductOptionExample/index.js +88 -88
  362. package/example/views/ProductShareExample/index.js +51 -51
  363. package/example/views/ProductsListExample/index.js +77 -77
  364. package/example/views/ProductsListingExample/index.js +47 -47
  365. package/example/views/ResetPasswordExample/index.js +60 -60
  366. package/example/views/SearchOptionsExample/index.js +42 -42
  367. package/example/views/SessionManager/index.js +122 -122
  368. package/example/views/Signup/index.js +64 -64
  369. package/example/views/UpsellingPageExample/index.js +35 -35
  370. package/example/views/UserDetailsExample/index.js +69 -69
  371. package/example/views/UserProfile/index.js +73 -73
  372. package/index-example.js +23 -23
  373. package/index.html +13 -13
  374. package/native/index.js +257 -257
  375. package/native/src/NativeStrategy/index.js +20 -20
  376. package/native/src/contexts/OrderingContext/index.js +85 -85
  377. package/package.json +92 -92
  378. package/src/components/AddressDetails/index.js +149 -149
  379. package/src/components/AddressForm/index.js +372 -372
  380. package/src/components/AddressList/index.js +254 -254
  381. package/src/components/Analitycs/index.js +119 -119
  382. package/src/components/AnalyticsSegment/index.js +145 -145
  383. package/src/components/AppleLogin/index.js +164 -164
  384. package/src/components/BaseComponent/index.js +52 -52
  385. package/src/components/BusinessAndProductList/index.js +986 -986
  386. package/src/components/BusinessBasicInformation/index.js +119 -119
  387. package/src/components/BusinessController/index.js +351 -351
  388. package/src/components/BusinessInformation/BusinessOption/index.js +86 -86
  389. package/src/components/BusinessInformation/index.js +93 -93
  390. package/src/components/BusinessList/index.js +670 -670
  391. package/src/components/BusinessMenuListing/index.js +99 -99
  392. package/src/components/BusinessProductsCategories/index.js +60 -60
  393. package/src/components/BusinessProductsSearch/index.js +54 -54
  394. package/src/components/BusinessReviews/index.js +187 -187
  395. package/src/components/BusinessSearchList/index.js +364 -364
  396. package/src/components/BusinessSortControl/index.js +100 -100
  397. package/src/components/BusinessTypeFilter/index.js +142 -142
  398. package/src/components/BusinessesMap/index.js +110 -110
  399. package/src/components/Cart/index.js +211 -211
  400. package/src/components/CartStoresListing/index.js +157 -157
  401. package/src/components/Checkout/index.js +636 -636
  402. package/src/components/CmsContent/index.js +97 -97
  403. package/src/components/Contacts/index.js +512 -512
  404. package/src/components/CouponControl/index.js +171 -171
  405. package/src/components/DragAndDrop/index.js +41 -41
  406. package/src/components/DriverList/index.js +112 -112
  407. package/src/components/DriverTips/index.js +141 -141
  408. package/src/components/Emitter/index.js +36 -36
  409. package/src/components/ExamineClick/index.js +40 -40
  410. package/src/components/FacebookLoginButton/index.js +214 -214
  411. package/src/components/FacebookPixel/index.js +148 -148
  412. package/src/components/FavoriteList/index.js +278 -278
  413. package/src/components/FirebaseGoogleLoginButton/index.js +93 -93
  414. package/src/components/FloatingButton/index.js +70 -70
  415. package/src/components/ForgotPasswordForm/index.js +180 -180
  416. package/src/components/GiftCard/GiftCardOrdersList/index.js +155 -155
  417. package/src/components/GiftCard/PurchaseGiftCard/index.js +127 -127
  418. package/src/components/GiftCard/RedeemGiftCard/index.js +77 -77
  419. package/src/components/GiftCard/SendGiftCard/index.js +83 -83
  420. package/src/components/GoogleAutocompleteInput/index.js +154 -154
  421. package/src/components/GoogleIdentity/index.js +144 -144
  422. package/src/components/GoogleLoginButton/index.js +276 -276
  423. package/src/components/GoogleMaps/index.js +499 -499
  424. package/src/components/GpsButton/index.js +127 -127
  425. package/src/components/LanguageSelector/index.js +163 -163
  426. package/src/components/LoginForm/index.js +527 -527
  427. package/src/components/LogoutAction/index.js +211 -211
  428. package/src/components/MainSearch/index.js +149 -149
  429. package/src/components/MapView/index.js +116 -116
  430. package/src/components/MenuControl/index.js +238 -238
  431. package/src/components/Messages/index.js +166 -166
  432. package/src/components/MomentOption/index.js +322 -322
  433. package/src/components/MultiCartCreate/index.js +70 -70
  434. package/src/components/MultiCartsPaymethodsAndWallets/index.js +201 -201
  435. package/src/components/MultiCheckout/index.js +378 -378
  436. package/src/components/MultiOrdersDetails/index.js +109 -109
  437. package/src/components/MyOrders/index.js +150 -150
  438. package/src/components/MyOrdersList/index.js +104 -104
  439. package/src/components/NewOrderNotification/index.js +30 -30
  440. package/src/components/OrderChange/index.js +128 -128
  441. package/src/components/OrderDetails/index.js +684 -684
  442. package/src/components/OrderList/index.js +814 -814
  443. package/src/components/OrderListGroups/index.js +1245 -1245
  444. package/src/components/OrderReview/index.js +180 -180
  445. package/src/components/OrderTypeControl/index.js +75 -75
  446. package/src/components/OrderVerticalList/index.js +422 -422
  447. package/src/components/OrdersControlFilters/index.js +75 -75
  448. package/src/components/OrdersDashboardComponents/Appointments/index.js +72 -72
  449. package/src/components/OrdersDashboardComponents/BusinessProductsListing/index.js +629 -629
  450. package/src/components/OrdersDashboardComponents/CheckPassword/index.js +177 -177
  451. package/src/components/OrdersDashboardComponents/CityList/index.js +98 -98
  452. package/src/components/OrdersDashboardComponents/CountryList/index.js +162 -162
  453. package/src/components/OrdersDashboardComponents/CustomOrderDetails/index.js +238 -238
  454. package/src/components/OrdersDashboardComponents/DashboardBusinessList/index.js +617 -617
  455. package/src/components/OrdersDashboardComponents/DashboardOrdersList/index.js +943 -943
  456. package/src/components/OrdersDashboardComponents/DriversList/index.js +448 -448
  457. package/src/components/OrdersDashboardComponents/ExportCSV/index.js +192 -192
  458. package/src/components/OrdersDashboardComponents/GiftCardsList/index.js +189 -189
  459. package/src/components/OrdersDashboardComponents/GoogleMapsApiKeySetting/index.js +77 -77
  460. package/src/components/OrdersDashboardComponents/LogisticInformation/index.js +97 -97
  461. package/src/components/OrdersDashboardComponents/Logistics/index.js +174 -174
  462. package/src/components/OrdersDashboardComponents/Messages/index.js +384 -384
  463. package/src/components/OrdersDashboardComponents/MetaFields/index.js +186 -186
  464. package/src/components/OrdersDashboardComponents/OrderDetails/index.js +404 -404
  465. package/src/components/OrdersDashboardComponents/OrderNotification/index.js +70 -70
  466. package/src/components/OrdersDashboardComponents/OrdersFilter/index.js +362 -362
  467. package/src/components/OrdersDashboardComponents/OrdersManage/index.js +873 -873
  468. package/src/components/OrdersDashboardComponents/PointsWalletLevels/index.js +123 -123
  469. package/src/components/OrdersDashboardComponents/ReviewCustomer/index.js +113 -113
  470. package/src/components/OrdersDashboardComponents/Schedule/index.js +315 -315
  471. package/src/components/OrdersDashboardComponents/SettingsList/index.js +298 -298
  472. package/src/components/OrdersDashboardComponents/UserFormDetails/index.js +463 -463
  473. package/src/components/OrdersDashboardComponents/UsersList/index.js +944 -944
  474. package/src/components/OrdersDashboardComponents/WebsocketStatus/index.js +77 -77
  475. package/src/components/OrdersDashboardComponents/index.js +57 -57
  476. package/src/components/PageBanner/index.js +107 -107
  477. package/src/components/PaymentOptionCash/index.js +74 -74
  478. package/src/components/PaymentOptionPaypal/index.js +146 -146
  479. package/src/components/PaymentOptionSquare/index.js +336 -336
  480. package/src/components/PaymentOptionStripe/index.js +289 -289
  481. package/src/components/PaymentOptionStripeDirect/index.js +116 -116
  482. package/src/components/PaymentOptionStripeRedirect/StripeRedirectForm/index.js +71 -71
  483. package/src/components/PaymentOptionStripeRedirect/index.js +122 -122
  484. package/src/components/PaymentOptionWallet/index.js +185 -185
  485. package/src/components/PaymentOptions/index.js +262 -262
  486. package/src/components/PaymethodList/index.js +119 -119
  487. package/src/components/PhoneAutocomplete/index.js +318 -318
  488. package/src/components/PlaceSpot/index.js +183 -183
  489. package/src/components/Popup/index.js +169 -169
  490. package/src/components/ProductComponent/index.js +269 -269
  491. package/src/components/ProductForm/index.js +1105 -1105
  492. package/src/components/ProductImages/index.js +64 -64
  493. package/src/components/ProductIngredient/index.js +72 -72
  494. package/src/components/ProductItemAccordion/index.js +72 -72
  495. package/src/components/ProductOption/index.js +42 -42
  496. package/src/components/ProductOptionSuboption/index.js +181 -181
  497. package/src/components/ProductShare/index.js +97 -97
  498. package/src/components/ProductsList/index.js +74 -74
  499. package/src/components/ProductsListing/index.js +166 -166
  500. package/src/components/ProfessionalInfo/index.js +156 -156
  501. package/src/components/PromotionsController/index.js +123 -123
  502. package/src/components/QueryLoginSpoonity/index.js +159 -159
  503. package/src/components/ReCaptcha/index.js +53 -53
  504. package/src/components/ResetPassword/index.js +111 -111
  505. package/src/components/ReviewCustomer/index.js +117 -117
  506. package/src/components/ReviewDriver/index.js +157 -157
  507. package/src/components/ReviewProduct/index.js +162 -162
  508. package/src/components/SearchOptions/index.js +69 -69
  509. package/src/components/Sessions/index.js +217 -217
  510. package/src/components/SignupForm/index.js +555 -555
  511. package/src/components/SingleBusinessCard/index.js +80 -80
  512. package/src/components/SingleOrderCard/index.js +160 -160
  513. package/src/components/SingleProductCard/index.js +130 -130
  514. package/src/components/SingleProfessionalCard/index.js +121 -121
  515. package/src/components/SmartAppBanner/index.js +71 -71
  516. package/src/components/StoreProductList/index.js +303 -303
  517. package/src/components/StripeElementsForm/CardForm/index.js +248 -248
  518. package/src/components/StripeElementsForm/index.js +78 -78
  519. package/src/components/UpsellingPage/index.js +120 -120
  520. package/src/components/UserFormDetails/index.js +742 -742
  521. package/src/components/UserVerification/index.js +242 -242
  522. package/src/components/WalletList/index.js +160 -160
  523. package/src/components/WebsocketStatus/index.js +80 -80
  524. package/src/components/WrapperGoogleMaps/index.js +67 -67
  525. package/src/constants/code-numbers.js +219 -219
  526. package/src/constants/timezones.js +427 -427
  527. package/src/contexts/ApiContext/index.js +59 -59
  528. package/src/contexts/BillingContext/index.js +28 -28
  529. package/src/contexts/BusinessContext/index.js +71 -71
  530. package/src/contexts/ConfigContext/index.js +217 -217
  531. package/src/contexts/CustomerContext/index.js +69 -69
  532. package/src/contexts/EventContext/index.js +31 -31
  533. package/src/contexts/LanguageContext/index.js +144 -144
  534. package/src/contexts/OptimizationLoadContext/index.js +95 -95
  535. package/src/contexts/OrderContext/index.js +1450 -1450
  536. package/src/contexts/OrderingContext/index.js +86 -86
  537. package/src/contexts/OrderingThemeContext/index.js +107 -107
  538. package/src/contexts/ProductContext/index.js +62 -62
  539. package/src/contexts/SessionContext/index.js +172 -172
  540. package/src/contexts/SiteContext/index.js +79 -79
  541. package/src/contexts/ToastContext/index.js +42 -42
  542. package/src/contexts/UtilsContext/index.js +352 -352
  543. package/src/contexts/ValidationsFieldsContext/index.js +65 -65
  544. package/src/contexts/WebsocketContext/index.js +91 -91
  545. package/src/contexts/WebsocketContext/socket.js +92 -92
  546. package/src/index.js +371 -371
  547. package/src/utils/index.js +32 -32
  548. package/src/webStrategy/index.js +18 -18
  549. package/webpack.dev.js +41 -41
  550. package/webpack.prod.js +64 -64
  551. package/_bundles/0.ordering-component.aa21761da1477298dd80.js +0 -1
  552. package/_bundles/7.ordering-component.aa21761da1477298dd80.js +0 -2
  553. package/_bundles/7.ordering-component.aa21761da1477298dd80.js.LICENSE.txt +0 -53
  554. package/_bundles/ordering-component.aa21761da1477298dd80.js +0 -2
@@ -1,1105 +1,1105 @@
1
- import React, { useState, useEffect } from 'react'
2
- import PropTypes from 'prop-types'
3
- import moment from 'moment'
4
- import { useOrder } from '../../contexts/OrderContext'
5
- import { useConfig } from '../../contexts/ConfigContext'
6
- import { useApi } from '../../contexts/ApiContext'
7
- import { useEvent } from '../../contexts/EventContext'
8
- import { useSession } from '../../contexts/SessionContext'
9
- import { ToastType, useToast } from '../../contexts/ToastContext'
10
- import { useLanguage } from '../../contexts/LanguageContext'
11
- import { useWebsocket } from '../../contexts/WebsocketContext'
12
-
13
- export const ProductForm = (props) => {
14
- const {
15
- UIComponent,
16
- useOrderContext,
17
- onSave,
18
- handleCustomSave,
19
- isStarbucks,
20
- isService,
21
- isCartProduct,
22
- productAddedToCartLength,
23
- professionalList,
24
- handleUpdateProducts,
25
- handleUpdateProfessionals,
26
- handleChangeProfessional,
27
- setProductLoading
28
- } = props
29
-
30
- const requestsState = {}
31
-
32
- const [{ user, token }, { login }] = useSession()
33
- const [, { showToast }] = useToast()
34
- const [, t] = useLanguage()
35
-
36
- const [ordering] = useApi()
37
- const socket = useWebsocket()
38
- /**
39
- * Events context
40
- */
41
- const [events] = useEvent()
42
- /**
43
- * Original product state
44
- */
45
- const availableLazyLoad = props.product?.load_type === 'lazy' && props.businessId && props.categoryId && props.productId
46
- const [product, setProduct] = useState({ product: availableLazyLoad ? null : props.product, loading: false, error: null })
47
-
48
- /**
49
- * Product cart state
50
- */
51
- const [productCart, setProductCart] = useState({ ingredients: {}, options: {} })
52
-
53
- /**
54
- * Errors state
55
- */
56
- const [errors, setErrors] = useState({})
57
-
58
- /**
59
- * Suboption by default when there is only one
60
- */
61
- const [defaultSubOptions, setDefaultSubOptions] = useState([])
62
-
63
- /**
64
- * Custom Suboption by default
65
- */
66
- const [customDefaultSubOptions, setCustomDefaultSubOptions] = useState([])
67
-
68
- /**
69
- * preselected and selected suboptions
70
- */
71
- const [selectedSuboptions, setSelectedSuboptions] = useState([])
72
-
73
- /**
74
- * dictionary of respect_to suboptions
75
- */
76
- const [dependsSuboptions, setDependsSuboptions] = useState([])
77
-
78
- const [professionalListState, setProfessionalListState] = useState({ loading: false, professionals: [], error: null })
79
-
80
- /**
81
- * Action status
82
- */
83
- const [actionStatus, setActionStatus] = useState({ loading: false, error: null })
84
-
85
- /**
86
- * pizza type and position
87
- */
88
- const [pizzaState, setPizzaState] = useState({})
89
-
90
- /**
91
- * Edit mode
92
- */
93
- const editMode = typeof props.productCart?.code !== 'undefined'
94
-
95
- /**
96
- * Order context manager
97
- */
98
- const [orderState, { addProduct, updateProduct, addMultiProduct }] = useOrder()
99
-
100
- /**
101
- * Remove to balances in edit mode
102
- */
103
- const removeToBalance = editMode ? props.productCart.quantity : 0
104
-
105
- /**
106
- * Current cart
107
- */
108
- const cart = orderState.carts?.[`businessId:${props.businessId}`]
109
-
110
- /**
111
- * Total products in cart
112
- */
113
- const cartProducts = Object.values(orderState.carts).reduce((products, _cart) => [...products, ..._cart?.products], [])
114
-
115
- /**
116
- * Total the current product in cart
117
- */
118
- const productBalance = cartProducts.reduce((sum, _product) => sum + (product.product && _product.id === product.product.id ? _product.quantity : 0), 0)
119
-
120
- /**
121
- * Total product in cart
122
- */
123
- const totalBalance = (productBalance || 0) - removeToBalance
124
-
125
- /**
126
- * Config context manager
127
- */
128
- const [stateConfig] = useConfig()
129
-
130
- /**
131
- * Max total product in cart by config
132
- */
133
- const maxCartProductConfig = (stateConfig.configs.max_product_amount ? parseInt(stateConfig.configs.max_product_amount) : 100) - totalBalance
134
-
135
- /**
136
- * Max total product in cart by config
137
- */
138
- let maxCartProductInventory = (product.product?.inventoried ? product.product?.quantity : undefined) - totalBalance
139
-
140
- /**
141
- * True if product is sold out
142
- */
143
- const isSoldOut = product.product?.inventoried && product.product?.quantity === 0
144
-
145
- /**
146
- * Fix if maxCartProductInventory is not valid
147
- */
148
- maxCartProductInventory = !isNaN(maxCartProductInventory) ? maxCartProductInventory : maxCartProductConfig
149
-
150
- /**
151
- * Max product quantity
152
- */
153
- const maxProductQuantity = Math.min(maxCartProductConfig, maxCartProductInventory)
154
-
155
- /**
156
- * alsea validation
157
- */
158
- const isAlsea = ['alsea', 'alsea-staging'].includes(ordering.project)
159
-
160
- /**
161
- * alsea custom options
162
- */
163
- const quesoYSalsaOptions = ['queso y salsa', 'queso mozzarella y salsa']
164
-
165
- /**
166
- * Init product cart status
167
- * @param {object} product Product to init product cart status
168
- */
169
- const initProductCart = (product) => {
170
- const ingredients = {}
171
- for (const key in product.ingredients) {
172
- const ingredient = product.ingredients[key]
173
- ingredients[`id:${ingredient.id}`] = {
174
- selected: true
175
- }
176
- }
177
- const quantity = (productAddedToCartLength && product?.maximum_per_order) ? (product?.maximum_per_order - productAddedToCartLength) : props.productCart?.quantity
178
- const newProductCart = {
179
- ...props.productCart,
180
- id: product.id,
181
- price: product.price,
182
- name: product.name,
183
- businessId: props.businessId,
184
- categoryId: product.category_id,
185
- inventoried: product.inventoried,
186
- stock: product.quantity,
187
- ingredients: props.productCart?.ingredients || ingredients,
188
- options: props.productCart?.options || {},
189
- comment: props.productCart?.comment || null,
190
- quantity: quantity || 1,
191
- favorite: product?.favorite
192
- }
193
- newProductCart.unitTotal = getUnitTotal(newProductCart)
194
- newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
195
- setProductCart(newProductCart)
196
- }
197
-
198
- /**
199
- * Get unit total for product cart
200
- * @param {object} productCart Current product status
201
- */
202
- const getUnitTotal = (productCart) => {
203
- let subtotal = 0
204
- for (let i = 0; i < product.product?.extras?.length; i++) {
205
- const extra = product.product?.extras[i]
206
- for (let j = 0; j < extra.options?.length; j++) {
207
- const option = extra.options[j]
208
- for (let k = 0; k < option.suboptions?.length; k++) {
209
- const suboption = option.suboptions[k]
210
- if (productCart.options[`id:${option.id}`]?.suboptions[`id:${suboption.id}`]?.selected) {
211
- const suboptionState = productCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
212
- const quantity = option.allow_suboption_quantity ? suboptionState.quantity : 1
213
- const price = option.with_half_option && suboption.half_price && suboptionState.position !== 'whole' ? suboption.half_price : suboption.price
214
- subtotal += price * quantity
215
- }
216
- }
217
- }
218
- }
219
- return product.product?.price + subtotal
220
- }
221
- /**
222
- * Method to add, remove favorite info for user from API
223
- */
224
- const handleFavoriteProduct = async (productFav, isAdd = false) => {
225
- if (!product || !user) return
226
- showToast(ToastType.Info, t('LOADING', 'loading'))
227
- try {
228
- setProduct({ ...product, loading: true, error: null })
229
- const productId = productFav?.id
230
- const changes = { object_id: productId }
231
- const requestOptions = {
232
- method: isAdd ? 'POST' : 'DELETE',
233
- headers: {
234
- 'Content-Type': 'application/json',
235
- Authorization: `Bearer ${token}`,
236
- 'X-App-X': ordering.appId,
237
- 'X-Socket-Id-X': socket?.getId()
238
- },
239
- ...(isAdd && { body: JSON.stringify(changes) })
240
- }
241
- const fetchEndpoint = isAdd
242
- ? `${ordering.root}/users/${user?.id}/favorite_products`
243
- : `${ordering.root}/users/${user.id}/favorite_products/${productId}`
244
- const response = await fetch(fetchEndpoint, requestOptions)
245
- const content = await response.json()
246
- if (!content.error) {
247
- loadProductWithOptions()
248
- handleUpdateProducts && handleUpdateProducts(productId, { favorite: isAdd })
249
- showToast(ToastType.Success, isAdd ? t('FAVORITE_ADDED', 'Favorite added') : t('FAVORITE_REMOVED', 'Favorite removed'))
250
- } else {
251
- setProduct({
252
- ...product,
253
- loading: false,
254
- error: content.result
255
- })
256
- showToast(ToastType.Error, content.result)
257
- }
258
- } catch (error) {
259
- setProduct({
260
- ...product,
261
- loading: false,
262
- error: [error.message]
263
- })
264
- showToast(ToastType.Error, [error.message])
265
- }
266
- }
267
-
268
- /**
269
- * Load product from API
270
- */
271
- const loadProductWithOptions = async () => {
272
- try {
273
- setProduct({ ...product, loading: true })
274
- const source = {}
275
- requestsState.product = source
276
- const parameters = {
277
- version: 'v2'
278
- }
279
- const { content: { result, error } } = await ordering
280
- .businesses(props.businessId)
281
- .categories(props.categoryId)
282
- .products(props.productId)
283
- .parameters(parameters)
284
- .get({ cancelToken: source })
285
-
286
- if (!error) {
287
- setProduct({
288
- ...product,
289
- loading: false,
290
- product: result
291
- })
292
- return
293
- }
294
- setProduct({
295
- ...product,
296
- loading: false,
297
- error: [result]
298
- })
299
- } catch (err) {
300
- setProduct({
301
- ...product,
302
- loading: false,
303
- error: [err.message]
304
- })
305
- }
306
- }
307
-
308
- /**
309
- * Remove related option by respect_to
310
- * @param {object} cart Current cart
311
- * @param {number} suboptionId Suboption id
312
- */
313
- const removeRelatedOptions = (productCart, suboptionId) => {
314
- product.product.extras.forEach(_extra => {
315
- _extra.options.forEach(_option => {
316
- if (_option.respect_to === suboptionId) {
317
- const suboptions = productCart.options[`id:${_option.id}`]?.suboptions
318
- if (suboptions) {
319
- Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(productCart, parseInt(suboptionKey.split(':')[1])))
320
- }
321
- if (productCart.options[`id:${_option.id}`]) {
322
- productCart.options[`id:${_option.id}`].suboptions = {}
323
- pizzaState[`option:${_option.id}`] = {}
324
- }
325
- }
326
- })
327
- })
328
- }
329
-
330
- /**
331
- * Get changes for ingredients state
332
- * @param {object} state Current ingrediente state
333
- * @param {object} ingredient Current ingredient
334
- */
335
- const handleChangeIngredientState = (state, ingredient) => {
336
- productCart.ingredients[`id:${ingredient.id}`] = state
337
- setProductCart({
338
- ...productCart,
339
- ingredients: productCart.ingredients
340
- })
341
- }
342
-
343
- /**
344
- * Change product state with new suboption state
345
- * @param {object} state New state with changes
346
- * @param {object} suboption Suboption object
347
- * @param {objecrt} option Option object
348
- * @param {object} product Product object
349
- */
350
- const handleChangeSuboptionState = (state, suboption, option) => {
351
- const newProductCart = JSON.parse(JSON.stringify(productCart))
352
- if (!newProductCart.options) {
353
- newProductCart.options = {}
354
- }
355
- if (!newProductCart.options[`id:${option.id}`]) {
356
- newProductCart.options[`id:${option.id}`] = {
357
- id: option.id,
358
- name: option.name,
359
- suboptions: {}
360
- }
361
- }
362
- let newPizzaState = {}
363
-
364
- if (!state.selected) {
365
- delete newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
366
- removeRelatedOptions(newProductCart, suboption.id)
367
- newPizzaState = handleVerifyPizzaState(state, suboption, option)
368
- } else {
369
- if (option.min === option.max && option.min === 1) {
370
- const suboptions = newProductCart.options[`id:${option.id}`].suboptions
371
- if (suboptions) {
372
- Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(newProductCart, parseInt(suboptionKey.split(':')[1])))
373
- }
374
- if (newProductCart.options[`id:${option.id}`]) {
375
- newProductCart.options[`id:${option.id}`].suboptions = {}
376
- }
377
- }
378
- newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`] = state
379
- }
380
- let suboptionsArray = []
381
- const _selectedSuboptions = selectedSuboptions
382
- if (state.selected) {
383
- for (const extra of product.product.extras) {
384
- for (const option of extra.options) {
385
- if (Object.keys(newProductCart?.options[`id:${option?.id}`]?.suboptions || {})?.length === 0) {
386
- delete newProductCart?.options[`id:${option?.id}`]
387
- }
388
- }
389
- }
390
- if (newProductCart?.options) {
391
- for (const extra of product.product.extras) {
392
- for (const option of extra.options) {
393
- for (const suboption of option.suboptions) {
394
- if (newProductCart?.options[`id:${option?.id}`]) {
395
- if (newProductCart?.options[`id:${option?.id}`]?.suboptions[`id:${suboption?.id}`]) {
396
- _selectedSuboptions[`suboption:${suboption.id}`] = true
397
- } else {
398
- _selectedSuboptions[`suboption:${suboption.id}`] = false
399
- }
400
- } else {
401
- _selectedSuboptions[`suboption:${suboption.id}`] = suboption?.preselected || (option?.max === 1 && option?.min === 1 && option?.suboptions?.length === 1)
402
- }
403
- }
404
- }
405
- }
406
- }
407
- const preselectedOptions = []
408
- const preselectedSuboptions = []
409
- for (const extra of product.product.extras) {
410
- for (const option of extra.options) {
411
- for (const suboption of option.suboptions) {
412
- if (checkSuboptionsSelected(suboption?.id, _selectedSuboptions, dependsSuboptions)) {
413
- preselectedOptions.push(option)
414
- preselectedSuboptions.push(suboption)
415
- }
416
- }
417
- }
418
- }
419
-
420
- const states = preselectedSuboptions.map((suboption, i) => {
421
- const cartSuboption = newProductCart?.options[`id:${preselectedOptions[i]?.id}`]?.suboptions[`id:${suboption?.id}`] || suboption
422
- const price = preselectedOptions[i]?.with_half_option && cartSuboption?.half_price && cartSuboption?.position !== 'whole'
423
- ? cartSuboption.half_price
424
- : cartSuboption.price
425
-
426
- return {
427
- id: cartSuboption.id,
428
- name: cartSuboption.name,
429
- position: state?.id === cartSuboption?.id ? state?.position : cartSuboption.position || 'whole',
430
- price: state?.id === cartSuboption?.id ? state.price : price,
431
- quantity: state?.id === cartSuboption?.id
432
- ? state.quantity
433
- : quesoYSalsaOptions.includes(preselectedOptions[i]?.name?.toLowerCase()) && isAlsea
434
- ? cartSuboption?.quantity ?? 1
435
- : cartSuboption?.quantity || 1,
436
- selected: true,
437
- total: state?.id === cartSuboption?.id ? state.total : price
438
- }
439
- })
440
- preselectedOptions.map((option, i) => {
441
- const defaultSuboption = {
442
- option: option,
443
- suboption: preselectedSuboptions[i],
444
- state: states[i]
445
- }
446
- suboptionsArray = [...suboptionsArray, defaultSuboption]
447
- })
448
- newPizzaState = handleVerifyPizzaState(state, suboption, option, preselectedOptions, preselectedSuboptions, states)
449
- }
450
-
451
- let newBalance = Object.keys(newProductCart.options[`id:${option.id}`].suboptions).length
452
- if (option.limit_suboptions_by_max) {
453
- newBalance = Object.values(newProductCart.options[`id:${option.id}`].suboptions).reduce((count, suboption) => {
454
- return count + suboption.quantity
455
- }, 0)
456
- }
457
- const hasPreselectedFlow = suboptionsArray.filter(state => state?.suboption?.preselected)
458
- if (newBalance <= option.max || (newPizzaState?.[`option:${option?.id}`]?.value <= option.max && option?.with_half_option)) {
459
- newProductCart.options[`id:${option.id}`].balance = newBalance
460
- newProductCart.unitTotal = getUnitTotal(newProductCart)
461
- newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
462
- if (state.selected && hasPreselectedFlow?.length > 0) {
463
- handleChangeSuboptionDefault(suboptionsArray, newPizzaState)
464
- setSelectedSuboptions(_selectedSuboptions)
465
- } else {
466
- setProductCart(newProductCart)
467
- }
468
- }
469
- }
470
-
471
- const handleChangeSuboptionDefault = (defaultOptions, newPizzaState) => {
472
- const newProductCart = JSON.parse(JSON.stringify(productCart))
473
- if (!newProductCart.options) {
474
- newProductCart.options = {}
475
- }
476
- defaultOptions.map(({ option, state, suboption }) => {
477
- if (!newProductCart.options[`id:${option.id}`]) {
478
- newProductCart.options[`id:${option.id}`] = {
479
- id: option.id,
480
- name: option.name,
481
- suboptions: {}
482
- }
483
- }
484
- if (!state?.selected) {
485
- delete newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
486
- removeRelatedOptions(newProductCart, suboption.id)
487
- } else {
488
- if ((option.min === option.max) && option.min === 1) {
489
- const suboptions = newProductCart.options[`id:${option.id}`].suboptions
490
- if (suboptions) {
491
- Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(newProductCart, parseInt(suboptionKey.split(':')[1])))
492
- }
493
- if (newProductCart.options[`id:${option.id}`]) {
494
- newProductCart.options[`id:${option.id}`].suboptions = {}
495
- }
496
- }
497
- newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`] = state
498
- }
499
- let newBalance = Object.keys(newProductCart.options[`id:${option.id}`].suboptions).length
500
- if (option.limit_suboptions_by_max) {
501
- newBalance = Object.values(newProductCart.options[`id:${option.id}`].suboptions).reduce((count, suboption) => {
502
- return count + suboption.quantity
503
- }, 0)
504
- }
505
-
506
- if (newBalance <= option.max || (newPizzaState?.[`option:${option.id}`]?.value <= option.max && option?.with_half_option)) {
507
- newProductCart.options[`id:${option.id}`].balance = newBalance
508
- newProductCart.unitTotal = getUnitTotal(newProductCart)
509
- newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
510
- }
511
- })
512
- setProductCart(newProductCart)
513
- }
514
-
515
- /**
516
- * Change product state with new comment state
517
- * @param {object} e Product comment
518
- */
519
- const handleChangeCommentState = (e) => {
520
- const comment = e.target.value ?? ''
521
- productCart.comment = comment.trim()
522
- setProductCart({
523
- ...productCart,
524
- comment: comment.trim()
525
- })
526
- }
527
-
528
- /**
529
- * Check options to get errors
530
- */
531
- const checkErrors = () => {
532
- const errors = {}
533
- if (!product?.product) {
534
- return errors
535
- }
536
- product.product?.extras?.forEach(extra => {
537
- extra.options.map(option => {
538
- const suboptions = productCart.options[`id:${option.id}`]?.suboptions
539
- const quantity = suboptions
540
- ? option?.with_half_option
541
- ? pizzaState?.[`option:${option?.id}`]?.value
542
- : (option.limit_suboptions_by_max
543
- ? Object.values(suboptions).reduce((count, suboption) => {
544
- return count + suboption.quantity
545
- }, 0)
546
- : Object.keys(suboptions)?.length)
547
- : 0
548
- let evaluateRespectTo = false
549
- if (option.respect_to && productCart.options) {
550
- const options = productCart?.options
551
- for (const key in options) {
552
- const _option = options[key]
553
- if (_option.suboptions[`id:${option.respect_to}`]?.selected) {
554
- evaluateRespectTo = true
555
- break
556
- }
557
- }
558
- }
559
- const evaluate = option.respect_to ? evaluateRespectTo : true
560
- if (option?.suboptions?.length > 0 && evaluate) {
561
- if (option.min > quantity) {
562
- errors[`id:${option.id}`] = true
563
- } else if (option.max < quantity && (option?.with_half_option && isAlsea && (option.max + 0.5 < quantity))) {
564
- errors[`id:${option.id}`] = true
565
- }
566
- }
567
- })
568
- })
569
- setErrors(errors)
570
- return errors
571
- }
572
-
573
- /**
574
- * Handle when click on save product
575
- */
576
- const handleSave = async (values) => {
577
- try {
578
- setProductLoading && setProductLoading(true)
579
- if (handleCustomSave) {
580
- handleCustomSave && handleCustomSave()
581
- }
582
- const errors = checkErrors()
583
- const isMultiProduct = JSON.parse(product?.product?.meta || '{}')?.external_type === 'coupon'
584
- const hasAlreadyCoupon = cart?.metafields?.find?.(meta => meta?.key === 'pulse_coupons')?.value && isMultiProduct
585
-
586
- if ((Object.keys(errors).length === 0 || isService) && !hasAlreadyCoupon) {
587
- let successful = true
588
- if (useOrderContext) {
589
- successful = false
590
- const changes = cart || { business_id: props.businessId }
591
- const currentProduct = !isService
592
- ? { ...productCart }
593
- : {
594
- ...productCart,
595
- professional_id: values?.professional?.id,
596
- service_start: values?.serviceTime ?? orderState.options?.moment
597
- }
598
- onSave(productCart, !props.productCart?.code)
599
- if (!props.productCart?.code) {
600
- successful =
601
- isMultiProduct
602
- ? await addMultiProduct(currentProduct, changes, false)
603
- : await addProduct(currentProduct, changes, false)
604
- } else {
605
- successful = await updateProduct(currentProduct, changes, false)
606
- if (successful) {
607
- events.emit('product_edited', currentProduct)
608
- }
609
- }
610
- }
611
- if (successful) {
612
- if (isService) {
613
- const updatedProfessional = JSON.parse(JSON.stringify(values?.professional))
614
- const duration = product?.product?.duration
615
- updatedProfessional.busy_times.push({
616
- start: values?.serviceTime,
617
- end: moment(values?.serviceTime).add(duration, 'minutes').format('YYYY-MM-DD HH:mm:ss'),
618
- duration
619
- })
620
- handleUpdateProfessionals && handleUpdateProfessionals(updatedProfessional)
621
- handleChangeProfessional && handleChangeProfessional(updatedProfessional)
622
- }
623
- } else {
624
- showToast(
625
- ToastType.Error,
626
- !props.productCart?.code ? t('FAILED_TO_ADD_PRODUCT', 'Failed to add product') : t('FAILED_TO_UPDATE_PRODUCT', 'Failed to update product'),
627
- 5000
628
- )
629
- }
630
- }
631
- if (hasAlreadyCoupon) {
632
- showToast(
633
- ToastType.Error,
634
- t('COUPON_ALREADY_ADDED', 'You have a coupon already added')
635
- )
636
- }
637
- setProductLoading && setProductLoading(false)
638
- } catch (err) {
639
- showToast(
640
- ToastType.Error,
641
- !props.productCart?.code ? t('FAILED_TO_ADD_PRODUCT', 'Failed to add product') : t('FAILED_TO_UPDATE_PRODUCT', 'Failed to update product')
642
- )
643
- setProductLoading && setProductLoading(false)
644
- }
645
- }
646
-
647
- const handleCreateGuestUser = async (values) => {
648
- try {
649
- setActionStatus({ ...actionStatus, loading: true })
650
- const { content: { error, result } } = await ordering.users().save(values)
651
- if (!error) {
652
- setActionStatus({ error: null, loading: false })
653
- login({
654
- user: result,
655
- token: result.session?.access_token
656
- })
657
- } else {
658
- setActionStatus({ error: result, loading: false })
659
- }
660
- } catch (err) {
661
- setActionStatus({ error: err.message, loading: false })
662
- }
663
- }
664
-
665
- const increment = () => {
666
- if (maxProductQuantity <= 0 || productCart.quantity >= maxProductQuantity) {
667
- return
668
- }
669
- productCart.quantity++
670
- productCart.unitTotal = getUnitTotal(productCart)
671
- productCart.total = productCart.unitTotal * productCart.quantity
672
- setProductCart({
673
- ...productCart
674
- })
675
- }
676
-
677
- const decrement = () => {
678
- if (productCart.quantity === 0) {
679
- return
680
- }
681
- productCart.quantity--
682
- productCart.unitTotal = getUnitTotal(productCart)
683
- productCart.total = productCart.unitTotal * productCart.quantity
684
- setProductCart({
685
- ...productCart
686
- })
687
- }
688
-
689
- const handleChangeProductCartQuantity = (quantity) => {
690
- if (maxProductQuantity <= 0 || quantity > maxProductQuantity) {
691
- return
692
- }
693
- productCart.quantity = quantity || 0
694
- productCart.total = productCart.unitTotal * productCart.quantity
695
- setProductCart({
696
- ...productCart
697
- })
698
- }
699
-
700
- /**
701
- * Check if option must show
702
- * @param {object} option Option to check
703
- */
704
- const showOption = (option) => {
705
- let showOption = true
706
- if (option.respect_to) {
707
- showOption = false
708
- if (productCart.options) {
709
- const options = productCart.options
710
- for (const key in options) {
711
- const _option = options[key]
712
- if (_option.suboptions[`id:${option.respect_to}`]?.selected) {
713
- showOption = true
714
- break
715
- }
716
- }
717
- }
718
- }
719
-
720
- if (option?.suboptions?.length === 0) showOption = false
721
-
722
- return showOption
723
- }
724
-
725
- /**
726
- * Load professionals from API
727
- */
728
- const getProfessionalList = async () => {
729
- try {
730
- setProfessionalListState({ ...professionalListState, loading: true })
731
- const { content: { result, error } } = await ordering
732
- .businesses(props.businessId)
733
- .select(['id', 'professionals'])
734
- .get()
735
-
736
- if (!error) {
737
- setProfessionalListState({
738
- ...professionalListState,
739
- loading: false,
740
- professionals: result?.professionals ?? []
741
- })
742
- return
743
- }
744
- setProfessionalListState({
745
- ...professionalListState,
746
- loading: false,
747
- error: [result]
748
- })
749
- } catch (err) {
750
- setProfessionalListState({
751
- ...professionalListState,
752
- loading: false,
753
- error: [err.message]
754
- })
755
- }
756
- }
757
- /**
758
- * function to verify position of pizza ingredients
759
- * @param {object} newProductCart cart updated with suboptions
760
- */
761
- const handleVerifyPizzaState = (state, suboption, option, preselectedOptions, preselectedSuboptions, states) => {
762
- let newPizzaState = {}
763
- if (state?.selected) {
764
- preselectedOptions.map((option, i) => {
765
- if (option?.with_half_option) {
766
- newPizzaState = {
767
- ...newPizzaState,
768
- [`option:${option?.id}`]: {
769
- ...newPizzaState?.[`option:${option?.id}`],
770
- [`suboption:${preselectedSuboptions[i]?.id}`]:
771
- isAlsea
772
- ? (states[i]?.position === 'whole' ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0)
773
- : (states[i]?.position === 'whole' ? 1 : 0.5) * states[i].quantity
774
- }
775
- }
776
- const suboptionValue = isAlsea
777
- ? ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0))
778
- : ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) * states[i].quantity)
779
-
780
- const value = suboptionValue + (newPizzaState[`option:${option?.id}`].value || 0)
781
- newPizzaState[`option:${option?.id}`].value = value
782
- }
783
- })
784
- } else {
785
- newPizzaState = {
786
- ...pizzaState,
787
- [`option:${option?.id}`]: {
788
- ...pizzaState[`option:${option?.id}`],
789
- value: 0
790
- }
791
- }
792
- delete newPizzaState?.[`option:${option?.id}`]?.[`suboption:${suboption?.id}`]
793
- const value = Object?.values(newPizzaState?.[`option:${option?.id}`] || {})?.reduce((acc, value) => acc + value, 0)
794
- newPizzaState[`option:${option?.id}`].value = value
795
- }
796
- setPizzaState(newPizzaState)
797
- return newPizzaState
798
- }
799
- /**
800
- * Init product cart when product changed
801
- */
802
- useEffect(() => {
803
- if (product.product) {
804
- initProductCart(product.product)
805
- }
806
- }, [product.product])
807
-
808
- /**
809
- * Check error when product state changed
810
- */
811
- useEffect(() => {
812
- checkErrors()
813
- }, [productCart])
814
-
815
- /**
816
- * Listening product changes
817
- */
818
- useEffect(() => {
819
- if (props?.product?.load_type === 'lazy') return
820
- setProduct({ ...product, product: props.product })
821
- }, [props.product])
822
-
823
- /**
824
- * Check if there is an option required with one suboption
825
- */
826
-
827
- const checkSuboptionsSelected = (suboptionId, _selectedSuboptions, _dependsSuboptions, count = 0) => {
828
- if (count > 100) {
829
- return false
830
- }
831
- if (!_selectedSuboptions[`suboption:${suboptionId}`]) {
832
- return false
833
- }
834
- const respectTo = _dependsSuboptions[`suboption:${suboptionId}`] ?? null
835
- if (respectTo === null) {
836
- return _selectedSuboptions[`suboption:${suboptionId}`]
837
- }
838
- return checkSuboptionsSelected(respectTo, _selectedSuboptions, _dependsSuboptions, count++)
839
- }
840
-
841
- useEffect(() => {
842
- if (!product?.loading && product?.product && product.product?.extras?.length > 0) {
843
- const _selectedSuboptions = {}
844
- const _dependsSuboptions = {}
845
- const preselectedOptions = []
846
- const preselectedSuboptions = []
847
- for (const extra of product.product.extras) {
848
- for (const option of extra.options) {
849
- for (const suboption of option.suboptions) {
850
- _selectedSuboptions[`suboption:${suboption.id}`] =
851
- (suboption.preselected ||
852
- (option?.max === 1 && option?.min === 1 && option?.suboptions?.length === 1)) &&
853
- (!editMode || !!props.productCart?.options[`id:${option?.id}`]?.suboptions[`id:${suboption?.id}`])
854
- _dependsSuboptions[`suboption:${suboption.id}`] = option?.conditioned && option?.respect_to !== null ? option?.respect_to : null
855
- }
856
- }
857
- }
858
-
859
- if (editMode && props?.productCart) {
860
- Object.values(props?.productCart?.options)?.map(option => Object.values(option?.suboptions)?.map(suboption => {
861
- _selectedSuboptions[`suboption:${suboption.id}`] = true
862
- }))
863
- }
864
-
865
- for (const extra of product.product.extras) {
866
- for (const option of extra.options) {
867
- for (const suboption of option.suboptions) {
868
- if (checkSuboptionsSelected(suboption?.id, _selectedSuboptions, _dependsSuboptions)) {
869
- preselectedOptions.push(option)
870
- preselectedSuboptions.push(suboption)
871
- }
872
- }
873
- }
874
- }
875
- setSelectedSuboptions(_selectedSuboptions)
876
- setDependsSuboptions(_dependsSuboptions)
877
- if (!preselectedOptions?.length) {
878
- return
879
- }
880
- let states = {}
881
- if (editMode && props?.productCart) {
882
- const cartSuboptions = Object.values(props?.productCart?.options)?.map(option => Object.values(option?.suboptions))?.flat()
883
- states = cartSuboptions.map((suboption, i) => {
884
- const price = preselectedOptions[i]?.with_half_option && suboption?.half_price && suboption?.position !== 'whole'
885
- ? suboption.half_price
886
- : suboption.price
887
- return {
888
- id: suboption.id,
889
- name: suboption.name,
890
- position: suboption.position || 'whole',
891
- price,
892
- quantity: suboption.quantity,
893
- selected: true,
894
- total: price
895
- }
896
- })
897
- } else {
898
- states = preselectedSuboptions.map((suboption, i) => {
899
- const price = preselectedOptions[i]?.with_half_option && suboption?.half_price && suboption?.position !== 'whole'
900
- ? suboption.half_price
901
- : suboption.price
902
-
903
- return {
904
- id: suboption.id,
905
- name: suboption.name,
906
- position: suboption.position || 'whole',
907
- price,
908
- quantity: 1,
909
- selected: true,
910
- total: price
911
- }
912
- })
913
- }
914
-
915
- let suboptionsArray = []
916
- let newPizzaState = {}
917
- preselectedOptions.map((option, i) => {
918
- const defaultSuboption = {
919
- option: option,
920
- suboption: preselectedSuboptions[i],
921
- state: states?.find((state) => state?.id === preselectedSuboptions[i]?.id)
922
- }
923
- suboptionsArray = [...suboptionsArray, defaultSuboption]
924
- if (option?.with_half_option) {
925
- newPizzaState = {
926
- ...newPizzaState,
927
- [`option:${option?.id}`]: {
928
- ...newPizzaState?.[`option:${option?.id}`],
929
- [`suboption:${preselectedSuboptions[i]?.id}`]: isAlsea
930
- ? (states[i]?.position === 'whole' ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0)
931
- : (states[i]?.position === 'whole' ? 1 : 0.5) * states[i].quantity
932
- }
933
- }
934
- const suboptionValue = isAlsea
935
- ? ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0))
936
- : ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) * states[i].quantity)
937
-
938
- const value = suboptionValue + (newPizzaState[`option:${option?.id}`].value || 0)
939
- newPizzaState[`option:${option?.id}`].value = value
940
- }
941
- })
942
- setPizzaState(newPizzaState)
943
- setDefaultSubOptions(suboptionsArray)
944
- setCustomDefaultSubOptions(suboptionsArray)
945
- }
946
- }, [JSON.stringify(product.product), product?.loading])
947
-
948
- if (isStarbucks) {
949
- useEffect(() => {
950
- if (product?.product && Object.keys(product?.product).length) {
951
- const options = [].concat(...product.product.extras.map(extra => extra.options.filter(
952
- option => (
953
- option.name === 'Tamaño' &&
954
- option.suboptions.filter(suboption => suboption.name === 'Grande (16oz - 437ml)').length === 1
955
- )
956
- )))
957
- if (!options?.length) {
958
- return
959
- }
960
- const suboptions = []
961
- .concat(...options.map(option => option.suboptions))
962
- .filter(suboption => suboption.name === 'Grande (16oz - 437ml)')
963
- const states = suboptions.map((suboption, i) => {
964
- const price = options[i].with_half_option && suboption.half_price && suboption?.position !== 'whole'
965
- ? suboption.half_price
966
- : suboption.price
967
-
968
- return {
969
- id: suboption.id,
970
- name: suboption.name,
971
- position: suboption.position || 'whole',
972
- price,
973
- quantity: 1,
974
- selected: true,
975
- total: price
976
- }
977
- })
978
- const defaultOptions = options.map((option, i) => {
979
- return {
980
- option: option,
981
- suboption: suboptions[i],
982
- state: states[i]
983
- }
984
- })
985
- setDefaultSubOptions([...customDefaultSubOptions, ...defaultOptions])
986
- }
987
- }, [customDefaultSubOptions])
988
- }
989
- /**
990
- * Check if defaultSubOption has content to set product Cart
991
- */
992
- useEffect(() => {
993
- if (defaultSubOptions?.length) {
994
- handleChangeSuboptionDefault(defaultSubOptions)
995
- }
996
- }, [JSON.stringify(defaultSubOptions)])
997
-
998
- /**
999
- * Load product on component mounted
1000
- */
1001
- useEffect(() => {
1002
- if (!props.product && (!props.businessId || !props.categoryId || !props.productId)) {
1003
- throw new Error('`businessId` && `categoryId` && `productId` are required if `product` was not provided.')
1004
- }
1005
- if ((props.product && props.product?.load_type === 'lazy' && props.businessId && props.categoryId && props.productId) ||
1006
- (!props.product && props.businessId && props.categoryId && props.productId)
1007
- ) {
1008
- loadProductWithOptions()
1009
- }
1010
- return () => {
1011
- if (requestsState.product) {
1012
- requestsState.product.cancel()
1013
- }
1014
- }
1015
- }, [])
1016
-
1017
- useEffect(() => {
1018
- if (!isService) return
1019
-
1020
- if (isCartProduct) {
1021
- getProfessionalList()
1022
- } else {
1023
- setProfessionalListState({
1024
- ...professionalListState,
1025
- professionals: professionalList
1026
- })
1027
- }
1028
- }, [isService, isCartProduct, professionalList])
1029
-
1030
- return (
1031
- <>
1032
- {
1033
- UIComponent && (
1034
- <UIComponent
1035
- {...props}
1036
- productObject={product}
1037
- productCart={productCart}
1038
- errors={errors}
1039
- editMode={editMode}
1040
- isSoldOut={isSoldOut}
1041
- actionStatus={actionStatus}
1042
- maxProductQuantity={maxProductQuantity}
1043
- pizzaState={pizzaState}
1044
- setPizzaState={setPizzaState}
1045
- increment={increment}
1046
- decrement={decrement}
1047
- handleChangeProductCartQuantity={handleChangeProductCartQuantity}
1048
- handleSave={handleSave}
1049
- showOption={showOption}
1050
- handleCreateGuestUser={handleCreateGuestUser}
1051
- handleFavoriteProduct={handleFavoriteProduct}
1052
- handleChangeIngredientState={handleChangeIngredientState}
1053
- handleChangeSuboptionState={handleChangeSuboptionState}
1054
- handleChangeCommentState={handleChangeCommentState}
1055
- professionalListState={professionalListState}
1056
- cart2={props.productCart}
1057
- isAlsea={isAlsea}
1058
- quesoYSalsaOptions={quesoYSalsaOptions}
1059
- />
1060
- )
1061
- }
1062
- </>
1063
- )
1064
- }
1065
-
1066
- ProductForm.propTypes = {
1067
- /**
1068
- * UI Component, this must be containt all graphic elements and use parent props
1069
- */
1070
- UIComponent: PropTypes.elementType,
1071
- /**
1072
- * `businessId`
1073
- */
1074
- businessId: PropTypes.number.isRequired,
1075
- /**
1076
- * `categoryId` is required if `product` prop is not present
1077
- */
1078
- categoryId: PropTypes.number,
1079
- /**
1080
- * `productId` is required if `product` prop is not present
1081
- */
1082
- productId: PropTypes.number,
1083
- /**
1084
- * `product` is required if `businessId`, `categoryId` or `productId` is not present
1085
- */
1086
- product: PropTypes.object,
1087
- /**
1088
- * Product from cart
1089
- */
1090
- productCart: PropTypes.object,
1091
- /**
1092
- * useOrderContext
1093
- */
1094
- useOrderContext: PropTypes.bool,
1095
- /**
1096
- * Function to save event
1097
- */
1098
- onSave: PropTypes.func
1099
- }
1100
-
1101
- ProductForm.defaultProps = {
1102
- productCart: {},
1103
- useOrderContext: true,
1104
- balance: 0
1105
- }
1
+ import React, { useState, useEffect } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import moment from 'moment'
4
+ import { useOrder } from '../../contexts/OrderContext'
5
+ import { useConfig } from '../../contexts/ConfigContext'
6
+ import { useApi } from '../../contexts/ApiContext'
7
+ import { useEvent } from '../../contexts/EventContext'
8
+ import { useSession } from '../../contexts/SessionContext'
9
+ import { ToastType, useToast } from '../../contexts/ToastContext'
10
+ import { useLanguage } from '../../contexts/LanguageContext'
11
+ import { useWebsocket } from '../../contexts/WebsocketContext'
12
+
13
+ export const ProductForm = (props) => {
14
+ const {
15
+ UIComponent,
16
+ useOrderContext,
17
+ onSave,
18
+ handleCustomSave,
19
+ isStarbucks,
20
+ isService,
21
+ isCartProduct,
22
+ productAddedToCartLength,
23
+ professionalList,
24
+ handleUpdateProducts,
25
+ handleUpdateProfessionals,
26
+ handleChangeProfessional,
27
+ setProductLoading
28
+ } = props
29
+
30
+ const requestsState = {}
31
+
32
+ const [{ user, token }, { login }] = useSession()
33
+ const [, { showToast }] = useToast()
34
+ const [, t] = useLanguage()
35
+
36
+ const [ordering] = useApi()
37
+ const socket = useWebsocket()
38
+ /**
39
+ * Events context
40
+ */
41
+ const [events] = useEvent()
42
+ /**
43
+ * Original product state
44
+ */
45
+ const availableLazyLoad = props.product?.load_type === 'lazy' && props.businessId && props.categoryId && props.productId
46
+ const [product, setProduct] = useState({ product: availableLazyLoad ? null : props.product, loading: false, error: null })
47
+
48
+ /**
49
+ * Product cart state
50
+ */
51
+ const [productCart, setProductCart] = useState({ ingredients: {}, options: {} })
52
+
53
+ /**
54
+ * Errors state
55
+ */
56
+ const [errors, setErrors] = useState({})
57
+
58
+ /**
59
+ * Suboption by default when there is only one
60
+ */
61
+ const [defaultSubOptions, setDefaultSubOptions] = useState([])
62
+
63
+ /**
64
+ * Custom Suboption by default
65
+ */
66
+ const [customDefaultSubOptions, setCustomDefaultSubOptions] = useState([])
67
+
68
+ /**
69
+ * preselected and selected suboptions
70
+ */
71
+ const [selectedSuboptions, setSelectedSuboptions] = useState([])
72
+
73
+ /**
74
+ * dictionary of respect_to suboptions
75
+ */
76
+ const [dependsSuboptions, setDependsSuboptions] = useState([])
77
+
78
+ const [professionalListState, setProfessionalListState] = useState({ loading: false, professionals: [], error: null })
79
+
80
+ /**
81
+ * Action status
82
+ */
83
+ const [actionStatus, setActionStatus] = useState({ loading: false, error: null })
84
+
85
+ /**
86
+ * pizza type and position
87
+ */
88
+ const [pizzaState, setPizzaState] = useState({})
89
+
90
+ /**
91
+ * Edit mode
92
+ */
93
+ const editMode = typeof props.productCart?.code !== 'undefined'
94
+
95
+ /**
96
+ * Order context manager
97
+ */
98
+ const [orderState, { addProduct, updateProduct, addMultiProduct }] = useOrder()
99
+
100
+ /**
101
+ * Remove to balances in edit mode
102
+ */
103
+ const removeToBalance = editMode ? props.productCart.quantity : 0
104
+
105
+ /**
106
+ * Current cart
107
+ */
108
+ const cart = orderState.carts?.[`businessId:${props.businessId}`]
109
+
110
+ /**
111
+ * Total products in cart
112
+ */
113
+ const cartProducts = Object.values(orderState.carts).reduce((products, _cart) => [...products, ..._cart?.products], [])
114
+
115
+ /**
116
+ * Total the current product in cart
117
+ */
118
+ const productBalance = cartProducts.reduce((sum, _product) => sum + (product.product && _product.id === product.product.id ? _product.quantity : 0), 0)
119
+
120
+ /**
121
+ * Total product in cart
122
+ */
123
+ const totalBalance = (productBalance || 0) - removeToBalance
124
+
125
+ /**
126
+ * Config context manager
127
+ */
128
+ const [stateConfig] = useConfig()
129
+
130
+ /**
131
+ * Max total product in cart by config
132
+ */
133
+ const maxCartProductConfig = (stateConfig.configs.max_product_amount ? parseInt(stateConfig.configs.max_product_amount) : 100) - totalBalance
134
+
135
+ /**
136
+ * Max total product in cart by config
137
+ */
138
+ let maxCartProductInventory = (product.product?.inventoried ? product.product?.quantity : undefined) - totalBalance
139
+
140
+ /**
141
+ * True if product is sold out
142
+ */
143
+ const isSoldOut = product.product?.inventoried && product.product?.quantity === 0
144
+
145
+ /**
146
+ * Fix if maxCartProductInventory is not valid
147
+ */
148
+ maxCartProductInventory = !isNaN(maxCartProductInventory) ? maxCartProductInventory : maxCartProductConfig
149
+
150
+ /**
151
+ * Max product quantity
152
+ */
153
+ const maxProductQuantity = Math.min(maxCartProductConfig, maxCartProductInventory)
154
+
155
+ /**
156
+ * alsea validation
157
+ */
158
+ const isAlsea = ['alsea', 'alsea-staging'].includes(ordering.project)
159
+
160
+ /**
161
+ * alsea custom options
162
+ */
163
+ const quesoYSalsaOptions = ['queso y salsa', 'queso mozzarella y salsa']
164
+
165
+ /**
166
+ * Init product cart status
167
+ * @param {object} product Product to init product cart status
168
+ */
169
+ const initProductCart = (product) => {
170
+ const ingredients = {}
171
+ for (const key in product.ingredients) {
172
+ const ingredient = product.ingredients[key]
173
+ ingredients[`id:${ingredient.id}`] = {
174
+ selected: true
175
+ }
176
+ }
177
+ const quantity = (productAddedToCartLength && product?.maximum_per_order) ? (product?.maximum_per_order - productAddedToCartLength) : props.productCart?.quantity
178
+ const newProductCart = {
179
+ ...props.productCart,
180
+ id: product.id,
181
+ price: product.price,
182
+ name: product.name,
183
+ businessId: props.businessId,
184
+ categoryId: product.category_id,
185
+ inventoried: product.inventoried,
186
+ stock: product.quantity,
187
+ ingredients: props.productCart?.ingredients || ingredients,
188
+ options: props.productCart?.options || {},
189
+ comment: props.productCart?.comment || null,
190
+ quantity: quantity || 1,
191
+ favorite: product?.favorite
192
+ }
193
+ newProductCart.unitTotal = getUnitTotal(newProductCart)
194
+ newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
195
+ setProductCart(newProductCart)
196
+ }
197
+
198
+ /**
199
+ * Get unit total for product cart
200
+ * @param {object} productCart Current product status
201
+ */
202
+ const getUnitTotal = (productCart) => {
203
+ let subtotal = 0
204
+ for (let i = 0; i < product.product?.extras?.length; i++) {
205
+ const extra = product.product?.extras[i]
206
+ for (let j = 0; j < extra.options?.length; j++) {
207
+ const option = extra.options[j]
208
+ for (let k = 0; k < option.suboptions?.length; k++) {
209
+ const suboption = option.suboptions[k]
210
+ if (productCart.options[`id:${option.id}`]?.suboptions[`id:${suboption.id}`]?.selected) {
211
+ const suboptionState = productCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
212
+ const quantity = option.allow_suboption_quantity ? suboptionState.quantity : 1
213
+ const price = option.with_half_option && suboption.half_price && suboptionState.position !== 'whole' ? suboption.half_price : suboption.price
214
+ subtotal += price * quantity
215
+ }
216
+ }
217
+ }
218
+ }
219
+ return product.product?.price + subtotal
220
+ }
221
+ /**
222
+ * Method to add, remove favorite info for user from API
223
+ */
224
+ const handleFavoriteProduct = async (productFav, isAdd = false) => {
225
+ if (!product || !user) return
226
+ showToast(ToastType.Info, t('LOADING', 'loading'))
227
+ try {
228
+ setProduct({ ...product, loading: true, error: null })
229
+ const productId = productFav?.id
230
+ const changes = { object_id: productId }
231
+ const requestOptions = {
232
+ method: isAdd ? 'POST' : 'DELETE',
233
+ headers: {
234
+ 'Content-Type': 'application/json',
235
+ Authorization: `Bearer ${token}`,
236
+ 'X-App-X': ordering.appId,
237
+ 'X-Socket-Id-X': socket?.getId()
238
+ },
239
+ ...(isAdd && { body: JSON.stringify(changes) })
240
+ }
241
+ const fetchEndpoint = isAdd
242
+ ? `${ordering.root}/users/${user?.id}/favorite_products`
243
+ : `${ordering.root}/users/${user.id}/favorite_products/${productId}`
244
+ const response = await fetch(fetchEndpoint, requestOptions)
245
+ const content = await response.json()
246
+ if (!content.error) {
247
+ loadProductWithOptions()
248
+ handleUpdateProducts && handleUpdateProducts(productId, { favorite: isAdd })
249
+ showToast(ToastType.Success, isAdd ? t('FAVORITE_ADDED', 'Favorite added') : t('FAVORITE_REMOVED', 'Favorite removed'))
250
+ } else {
251
+ setProduct({
252
+ ...product,
253
+ loading: false,
254
+ error: content.result
255
+ })
256
+ showToast(ToastType.Error, content.result)
257
+ }
258
+ } catch (error) {
259
+ setProduct({
260
+ ...product,
261
+ loading: false,
262
+ error: [error.message]
263
+ })
264
+ showToast(ToastType.Error, [error.message])
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Load product from API
270
+ */
271
+ const loadProductWithOptions = async () => {
272
+ try {
273
+ setProduct({ ...product, loading: true })
274
+ const source = {}
275
+ requestsState.product = source
276
+ const parameters = {
277
+ version: 'v2'
278
+ }
279
+ const { content: { result, error } } = await ordering
280
+ .businesses(props.businessId)
281
+ .categories(props.categoryId)
282
+ .products(props.productId)
283
+ .parameters(parameters)
284
+ .get({ cancelToken: source })
285
+
286
+ if (!error) {
287
+ setProduct({
288
+ ...product,
289
+ loading: false,
290
+ product: result
291
+ })
292
+ return
293
+ }
294
+ setProduct({
295
+ ...product,
296
+ loading: false,
297
+ error: [result]
298
+ })
299
+ } catch (err) {
300
+ setProduct({
301
+ ...product,
302
+ loading: false,
303
+ error: [err.message]
304
+ })
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Remove related option by respect_to
310
+ * @param {object} cart Current cart
311
+ * @param {number} suboptionId Suboption id
312
+ */
313
+ const removeRelatedOptions = (productCart, suboptionId) => {
314
+ product.product.extras.forEach(_extra => {
315
+ _extra.options.forEach(_option => {
316
+ if (_option.respect_to === suboptionId) {
317
+ const suboptions = productCart.options[`id:${_option.id}`]?.suboptions
318
+ if (suboptions) {
319
+ Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(productCart, parseInt(suboptionKey.split(':')[1])))
320
+ }
321
+ if (productCart.options[`id:${_option.id}`]) {
322
+ productCart.options[`id:${_option.id}`].suboptions = {}
323
+ pizzaState[`option:${_option.id}`] = {}
324
+ }
325
+ }
326
+ })
327
+ })
328
+ }
329
+
330
+ /**
331
+ * Get changes for ingredients state
332
+ * @param {object} state Current ingrediente state
333
+ * @param {object} ingredient Current ingredient
334
+ */
335
+ const handleChangeIngredientState = (state, ingredient) => {
336
+ productCart.ingredients[`id:${ingredient.id}`] = state
337
+ setProductCart({
338
+ ...productCart,
339
+ ingredients: productCart.ingredients
340
+ })
341
+ }
342
+
343
+ /**
344
+ * Change product state with new suboption state
345
+ * @param {object} state New state with changes
346
+ * @param {object} suboption Suboption object
347
+ * @param {objecrt} option Option object
348
+ * @param {object} product Product object
349
+ */
350
+ const handleChangeSuboptionState = (state, suboption, option) => {
351
+ const newProductCart = JSON.parse(JSON.stringify(productCart))
352
+ if (!newProductCart.options) {
353
+ newProductCart.options = {}
354
+ }
355
+ if (!newProductCart.options[`id:${option.id}`]) {
356
+ newProductCart.options[`id:${option.id}`] = {
357
+ id: option.id,
358
+ name: option.name,
359
+ suboptions: {}
360
+ }
361
+ }
362
+ let newPizzaState = {}
363
+
364
+ if (!state.selected) {
365
+ delete newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
366
+ removeRelatedOptions(newProductCart, suboption.id)
367
+ newPizzaState = handleVerifyPizzaState(state, suboption, option)
368
+ } else {
369
+ if (option.min === option.max && option.min === 1) {
370
+ const suboptions = newProductCart.options[`id:${option.id}`].suboptions
371
+ if (suboptions) {
372
+ Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(newProductCart, parseInt(suboptionKey.split(':')[1])))
373
+ }
374
+ if (newProductCart.options[`id:${option.id}`]) {
375
+ newProductCart.options[`id:${option.id}`].suboptions = {}
376
+ }
377
+ }
378
+ newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`] = state
379
+ }
380
+ let suboptionsArray = []
381
+ const _selectedSuboptions = selectedSuboptions
382
+ if (state.selected) {
383
+ for (const extra of product.product.extras) {
384
+ for (const option of extra.options) {
385
+ if (Object.keys(newProductCart?.options[`id:${option?.id}`]?.suboptions || {})?.length === 0) {
386
+ delete newProductCart?.options[`id:${option?.id}`]
387
+ }
388
+ }
389
+ }
390
+ if (newProductCart?.options) {
391
+ for (const extra of product.product.extras) {
392
+ for (const option of extra.options) {
393
+ for (const suboption of option.suboptions) {
394
+ if (newProductCart?.options[`id:${option?.id}`]) {
395
+ if (newProductCart?.options[`id:${option?.id}`]?.suboptions[`id:${suboption?.id}`]) {
396
+ _selectedSuboptions[`suboption:${suboption.id}`] = true
397
+ } else {
398
+ _selectedSuboptions[`suboption:${suboption.id}`] = false
399
+ }
400
+ } else {
401
+ _selectedSuboptions[`suboption:${suboption.id}`] = suboption?.preselected || (option?.max === 1 && option?.min === 1 && option?.suboptions?.length === 1)
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ const preselectedOptions = []
408
+ const preselectedSuboptions = []
409
+ for (const extra of product.product.extras) {
410
+ for (const option of extra.options) {
411
+ for (const suboption of option.suboptions) {
412
+ if (checkSuboptionsSelected(suboption?.id, _selectedSuboptions, dependsSuboptions)) {
413
+ preselectedOptions.push(option)
414
+ preselectedSuboptions.push(suboption)
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ const states = preselectedSuboptions.map((suboption, i) => {
421
+ const cartSuboption = newProductCart?.options[`id:${preselectedOptions[i]?.id}`]?.suboptions[`id:${suboption?.id}`] || suboption
422
+ const price = preselectedOptions[i]?.with_half_option && cartSuboption?.half_price && cartSuboption?.position !== 'whole'
423
+ ? cartSuboption.half_price
424
+ : cartSuboption.price
425
+
426
+ return {
427
+ id: cartSuboption.id,
428
+ name: cartSuboption.name,
429
+ position: state?.id === cartSuboption?.id ? state?.position : cartSuboption.position || 'whole',
430
+ price: state?.id === cartSuboption?.id ? state.price : price,
431
+ quantity: state?.id === cartSuboption?.id
432
+ ? state.quantity
433
+ : quesoYSalsaOptions.includes(preselectedOptions[i]?.name?.toLowerCase()) && isAlsea
434
+ ? cartSuboption?.quantity ?? 1
435
+ : cartSuboption?.quantity || 1,
436
+ selected: true,
437
+ total: state?.id === cartSuboption?.id ? state.total : price
438
+ }
439
+ })
440
+ preselectedOptions.map((option, i) => {
441
+ const defaultSuboption = {
442
+ option: option,
443
+ suboption: preselectedSuboptions[i],
444
+ state: states[i]
445
+ }
446
+ suboptionsArray = [...suboptionsArray, defaultSuboption]
447
+ })
448
+ newPizzaState = handleVerifyPizzaState(state, suboption, option, preselectedOptions, preselectedSuboptions, states)
449
+ }
450
+
451
+ let newBalance = Object.keys(newProductCart.options[`id:${option.id}`].suboptions).length
452
+ if (option.limit_suboptions_by_max) {
453
+ newBalance = Object.values(newProductCart.options[`id:${option.id}`].suboptions).reduce((count, suboption) => {
454
+ return count + suboption.quantity
455
+ }, 0)
456
+ }
457
+ const hasPreselectedFlow = suboptionsArray.filter(state => state?.suboption?.preselected)
458
+ if (newBalance <= option.max || (newPizzaState?.[`option:${option?.id}`]?.value <= option.max && option?.with_half_option)) {
459
+ newProductCart.options[`id:${option.id}`].balance = newBalance
460
+ newProductCart.unitTotal = getUnitTotal(newProductCart)
461
+ newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
462
+ if (state.selected && hasPreselectedFlow?.length > 0) {
463
+ handleChangeSuboptionDefault(suboptionsArray, newPizzaState)
464
+ setSelectedSuboptions(_selectedSuboptions)
465
+ } else {
466
+ setProductCart(newProductCart)
467
+ }
468
+ }
469
+ }
470
+
471
+ const handleChangeSuboptionDefault = (defaultOptions, newPizzaState) => {
472
+ const newProductCart = JSON.parse(JSON.stringify(productCart))
473
+ if (!newProductCart.options) {
474
+ newProductCart.options = {}
475
+ }
476
+ defaultOptions.map(({ option, state, suboption }) => {
477
+ if (!newProductCart.options[`id:${option.id}`]) {
478
+ newProductCart.options[`id:${option.id}`] = {
479
+ id: option.id,
480
+ name: option.name,
481
+ suboptions: {}
482
+ }
483
+ }
484
+ if (!state?.selected) {
485
+ delete newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`]
486
+ removeRelatedOptions(newProductCart, suboption.id)
487
+ } else {
488
+ if ((option.min === option.max) && option.min === 1) {
489
+ const suboptions = newProductCart.options[`id:${option.id}`].suboptions
490
+ if (suboptions) {
491
+ Object.keys(suboptions).map(suboptionKey => removeRelatedOptions(newProductCart, parseInt(suboptionKey.split(':')[1])))
492
+ }
493
+ if (newProductCart.options[`id:${option.id}`]) {
494
+ newProductCart.options[`id:${option.id}`].suboptions = {}
495
+ }
496
+ }
497
+ newProductCart.options[`id:${option.id}`].suboptions[`id:${suboption.id}`] = state
498
+ }
499
+ let newBalance = Object.keys(newProductCart.options[`id:${option.id}`].suboptions).length
500
+ if (option.limit_suboptions_by_max) {
501
+ newBalance = Object.values(newProductCart.options[`id:${option.id}`].suboptions).reduce((count, suboption) => {
502
+ return count + suboption.quantity
503
+ }, 0)
504
+ }
505
+
506
+ if (newBalance <= option.max || (newPizzaState?.[`option:${option.id}`]?.value <= option.max && option?.with_half_option)) {
507
+ newProductCart.options[`id:${option.id}`].balance = newBalance
508
+ newProductCart.unitTotal = getUnitTotal(newProductCart)
509
+ newProductCart.total = newProductCart.unitTotal * newProductCart.quantity
510
+ }
511
+ })
512
+ setProductCart(newProductCart)
513
+ }
514
+
515
+ /**
516
+ * Change product state with new comment state
517
+ * @param {object} e Product comment
518
+ */
519
+ const handleChangeCommentState = (e) => {
520
+ const comment = e.target.value ?? ''
521
+ productCart.comment = comment.trim()
522
+ setProductCart({
523
+ ...productCart,
524
+ comment: comment.trim()
525
+ })
526
+ }
527
+
528
+ /**
529
+ * Check options to get errors
530
+ */
531
+ const checkErrors = () => {
532
+ const errors = {}
533
+ if (!product?.product) {
534
+ return errors
535
+ }
536
+ product.product?.extras?.forEach(extra => {
537
+ extra.options.map(option => {
538
+ const suboptions = productCart.options[`id:${option.id}`]?.suboptions
539
+ const quantity = suboptions
540
+ ? option?.with_half_option
541
+ ? pizzaState?.[`option:${option?.id}`]?.value
542
+ : (option.limit_suboptions_by_max
543
+ ? Object.values(suboptions).reduce((count, suboption) => {
544
+ return count + suboption.quantity
545
+ }, 0)
546
+ : Object.keys(suboptions)?.length)
547
+ : 0
548
+ let evaluateRespectTo = false
549
+ if (option.respect_to && productCart.options) {
550
+ const options = productCart?.options
551
+ for (const key in options) {
552
+ const _option = options[key]
553
+ if (_option.suboptions[`id:${option.respect_to}`]?.selected) {
554
+ evaluateRespectTo = true
555
+ break
556
+ }
557
+ }
558
+ }
559
+ const evaluate = option.respect_to ? evaluateRespectTo : true
560
+ if (option?.suboptions?.length > 0 && evaluate) {
561
+ if (option.min > quantity) {
562
+ errors[`id:${option.id}`] = true
563
+ } else if (option.max < quantity && (option?.with_half_option && isAlsea && (option.max + 0.5 < quantity))) {
564
+ errors[`id:${option.id}`] = true
565
+ }
566
+ }
567
+ })
568
+ })
569
+ setErrors(errors)
570
+ return errors
571
+ }
572
+
573
+ /**
574
+ * Handle when click on save product
575
+ */
576
+ const handleSave = async (values) => {
577
+ try {
578
+ setProductLoading && setProductLoading(true)
579
+ if (handleCustomSave) {
580
+ handleCustomSave && handleCustomSave()
581
+ }
582
+ const errors = checkErrors()
583
+ const isMultiProduct = JSON.parse(product?.product?.meta || '{}')?.external_type === 'coupon'
584
+ const hasAlreadyCoupon = cart?.metafields?.find?.(meta => meta?.key === 'pulse_coupons')?.value && isMultiProduct
585
+
586
+ if ((Object.keys(errors).length === 0 || isService) && !hasAlreadyCoupon) {
587
+ let successful = true
588
+ if (useOrderContext) {
589
+ successful = false
590
+ const changes = cart || { business_id: props.businessId }
591
+ const currentProduct = !isService
592
+ ? { ...productCart }
593
+ : {
594
+ ...productCart,
595
+ professional_id: values?.professional?.id,
596
+ service_start: values?.serviceTime ?? orderState.options?.moment
597
+ }
598
+ onSave(productCart, !props.productCart?.code)
599
+ if (!props.productCart?.code) {
600
+ successful =
601
+ isMultiProduct
602
+ ? await addMultiProduct(currentProduct, changes, false)
603
+ : await addProduct(currentProduct, changes, false)
604
+ } else {
605
+ successful = await updateProduct(currentProduct, changes, false)
606
+ if (successful) {
607
+ events.emit('product_edited', currentProduct)
608
+ }
609
+ }
610
+ }
611
+ if (successful) {
612
+ if (isService) {
613
+ const updatedProfessional = JSON.parse(JSON.stringify(values?.professional))
614
+ const duration = product?.product?.duration
615
+ updatedProfessional.busy_times.push({
616
+ start: values?.serviceTime,
617
+ end: moment(values?.serviceTime).add(duration, 'minutes').format('YYYY-MM-DD HH:mm:ss'),
618
+ duration
619
+ })
620
+ handleUpdateProfessionals && handleUpdateProfessionals(updatedProfessional)
621
+ handleChangeProfessional && handleChangeProfessional(updatedProfessional)
622
+ }
623
+ } else {
624
+ showToast(
625
+ ToastType.Error,
626
+ !props.productCart?.code ? t('FAILED_TO_ADD_PRODUCT', 'Failed to add product') : t('FAILED_TO_UPDATE_PRODUCT', 'Failed to update product'),
627
+ 5000
628
+ )
629
+ }
630
+ }
631
+ if (hasAlreadyCoupon) {
632
+ showToast(
633
+ ToastType.Error,
634
+ t('COUPON_ALREADY_ADDED', 'You have a coupon already added')
635
+ )
636
+ }
637
+ setProductLoading && setProductLoading(false)
638
+ } catch (err) {
639
+ showToast(
640
+ ToastType.Error,
641
+ !props.productCart?.code ? t('FAILED_TO_ADD_PRODUCT', 'Failed to add product') : t('FAILED_TO_UPDATE_PRODUCT', 'Failed to update product')
642
+ )
643
+ setProductLoading && setProductLoading(false)
644
+ }
645
+ }
646
+
647
+ const handleCreateGuestUser = async (values) => {
648
+ try {
649
+ setActionStatus({ ...actionStatus, loading: true })
650
+ const { content: { error, result } } = await ordering.users().save(values)
651
+ if (!error) {
652
+ setActionStatus({ error: null, loading: false })
653
+ login({
654
+ user: result,
655
+ token: result.session?.access_token
656
+ })
657
+ } else {
658
+ setActionStatus({ error: result, loading: false })
659
+ }
660
+ } catch (err) {
661
+ setActionStatus({ error: err.message, loading: false })
662
+ }
663
+ }
664
+
665
+ const increment = () => {
666
+ if (maxProductQuantity <= 0 || productCart.quantity >= maxProductQuantity) {
667
+ return
668
+ }
669
+ productCart.quantity++
670
+ productCart.unitTotal = getUnitTotal(productCart)
671
+ productCart.total = productCart.unitTotal * productCart.quantity
672
+ setProductCart({
673
+ ...productCart
674
+ })
675
+ }
676
+
677
+ const decrement = () => {
678
+ if (productCart.quantity === 0) {
679
+ return
680
+ }
681
+ productCart.quantity--
682
+ productCart.unitTotal = getUnitTotal(productCart)
683
+ productCart.total = productCart.unitTotal * productCart.quantity
684
+ setProductCart({
685
+ ...productCart
686
+ })
687
+ }
688
+
689
+ const handleChangeProductCartQuantity = (quantity) => {
690
+ if (maxProductQuantity <= 0 || quantity > maxProductQuantity) {
691
+ return
692
+ }
693
+ productCart.quantity = quantity || 0
694
+ productCart.total = productCart.unitTotal * productCart.quantity
695
+ setProductCart({
696
+ ...productCart
697
+ })
698
+ }
699
+
700
+ /**
701
+ * Check if option must show
702
+ * @param {object} option Option to check
703
+ */
704
+ const showOption = (option) => {
705
+ let showOption = true
706
+ if (option.respect_to) {
707
+ showOption = false
708
+ if (productCart.options) {
709
+ const options = productCart.options
710
+ for (const key in options) {
711
+ const _option = options[key]
712
+ if (_option.suboptions[`id:${option.respect_to}`]?.selected) {
713
+ showOption = true
714
+ break
715
+ }
716
+ }
717
+ }
718
+ }
719
+
720
+ if (option?.suboptions?.length === 0) showOption = false
721
+
722
+ return showOption
723
+ }
724
+
725
+ /**
726
+ * Load professionals from API
727
+ */
728
+ const getProfessionalList = async () => {
729
+ try {
730
+ setProfessionalListState({ ...professionalListState, loading: true })
731
+ const { content: { result, error } } = await ordering
732
+ .businesses(props.businessId)
733
+ .select(['id', 'professionals'])
734
+ .get()
735
+
736
+ if (!error) {
737
+ setProfessionalListState({
738
+ ...professionalListState,
739
+ loading: false,
740
+ professionals: result?.professionals ?? []
741
+ })
742
+ return
743
+ }
744
+ setProfessionalListState({
745
+ ...professionalListState,
746
+ loading: false,
747
+ error: [result]
748
+ })
749
+ } catch (err) {
750
+ setProfessionalListState({
751
+ ...professionalListState,
752
+ loading: false,
753
+ error: [err.message]
754
+ })
755
+ }
756
+ }
757
+ /**
758
+ * function to verify position of pizza ingredients
759
+ * @param {object} newProductCart cart updated with suboptions
760
+ */
761
+ const handleVerifyPizzaState = (state, suboption, option, preselectedOptions, preselectedSuboptions, states) => {
762
+ let newPizzaState = {}
763
+ if (state?.selected) {
764
+ preselectedOptions.map((option, i) => {
765
+ if (option?.with_half_option) {
766
+ newPizzaState = {
767
+ ...newPizzaState,
768
+ [`option:${option?.id}`]: {
769
+ ...newPizzaState?.[`option:${option?.id}`],
770
+ [`suboption:${preselectedSuboptions[i]?.id}`]:
771
+ isAlsea
772
+ ? (states[i]?.position === 'whole' ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0)
773
+ : (states[i]?.position === 'whole' ? 1 : 0.5) * states[i].quantity
774
+ }
775
+ }
776
+ const suboptionValue = isAlsea
777
+ ? ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0))
778
+ : ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) * states[i].quantity)
779
+
780
+ const value = suboptionValue + (newPizzaState[`option:${option?.id}`].value || 0)
781
+ newPizzaState[`option:${option?.id}`].value = value
782
+ }
783
+ })
784
+ } else {
785
+ newPizzaState = {
786
+ ...pizzaState,
787
+ [`option:${option?.id}`]: {
788
+ ...pizzaState[`option:${option?.id}`],
789
+ value: 0
790
+ }
791
+ }
792
+ delete newPizzaState?.[`option:${option?.id}`]?.[`suboption:${suboption?.id}`]
793
+ const value = Object?.values(newPizzaState?.[`option:${option?.id}`] || {})?.reduce((acc, value) => acc + value, 0)
794
+ newPizzaState[`option:${option?.id}`].value = value
795
+ }
796
+ setPizzaState(newPizzaState)
797
+ return newPizzaState
798
+ }
799
+ /**
800
+ * Init product cart when product changed
801
+ */
802
+ useEffect(() => {
803
+ if (product.product) {
804
+ initProductCart(product.product)
805
+ }
806
+ }, [product.product])
807
+
808
+ /**
809
+ * Check error when product state changed
810
+ */
811
+ useEffect(() => {
812
+ checkErrors()
813
+ }, [productCart])
814
+
815
+ /**
816
+ * Listening product changes
817
+ */
818
+ useEffect(() => {
819
+ if (props?.product?.load_type === 'lazy') return
820
+ setProduct({ ...product, product: props.product })
821
+ }, [props.product])
822
+
823
+ /**
824
+ * Check if there is an option required with one suboption
825
+ */
826
+
827
+ const checkSuboptionsSelected = (suboptionId, _selectedSuboptions, _dependsSuboptions, count = 0) => {
828
+ if (count > 100) {
829
+ return false
830
+ }
831
+ if (!_selectedSuboptions[`suboption:${suboptionId}`]) {
832
+ return false
833
+ }
834
+ const respectTo = _dependsSuboptions[`suboption:${suboptionId}`] ?? null
835
+ if (respectTo === null) {
836
+ return _selectedSuboptions[`suboption:${suboptionId}`]
837
+ }
838
+ return checkSuboptionsSelected(respectTo, _selectedSuboptions, _dependsSuboptions, count++)
839
+ }
840
+
841
+ useEffect(() => {
842
+ if (!product?.loading && product?.product && product.product?.extras?.length > 0) {
843
+ const _selectedSuboptions = {}
844
+ const _dependsSuboptions = {}
845
+ const preselectedOptions = []
846
+ const preselectedSuboptions = []
847
+ for (const extra of product.product.extras) {
848
+ for (const option of extra.options) {
849
+ for (const suboption of option.suboptions) {
850
+ _selectedSuboptions[`suboption:${suboption.id}`] =
851
+ (suboption.preselected ||
852
+ (option?.max === 1 && option?.min === 1 && option?.suboptions?.length === 1)) &&
853
+ (!editMode || !!props.productCart?.options[`id:${option?.id}`]?.suboptions[`id:${suboption?.id}`])
854
+ _dependsSuboptions[`suboption:${suboption.id}`] = option?.conditioned && option?.respect_to !== null ? option?.respect_to : null
855
+ }
856
+ }
857
+ }
858
+
859
+ if (editMode && props?.productCart) {
860
+ Object.values(props?.productCart?.options)?.map(option => Object.values(option?.suboptions)?.map(suboption => {
861
+ _selectedSuboptions[`suboption:${suboption.id}`] = true
862
+ }))
863
+ }
864
+
865
+ for (const extra of product.product.extras) {
866
+ for (const option of extra.options) {
867
+ for (const suboption of option.suboptions) {
868
+ if (checkSuboptionsSelected(suboption?.id, _selectedSuboptions, _dependsSuboptions)) {
869
+ preselectedOptions.push(option)
870
+ preselectedSuboptions.push(suboption)
871
+ }
872
+ }
873
+ }
874
+ }
875
+ setSelectedSuboptions(_selectedSuboptions)
876
+ setDependsSuboptions(_dependsSuboptions)
877
+ if (!preselectedOptions?.length) {
878
+ return
879
+ }
880
+ let states = {}
881
+ if (editMode && props?.productCart) {
882
+ const cartSuboptions = Object.values(props?.productCart?.options)?.map(option => Object.values(option?.suboptions))?.flat()
883
+ states = cartSuboptions.map((suboption, i) => {
884
+ const price = preselectedOptions[i]?.with_half_option && suboption?.half_price && suboption?.position !== 'whole'
885
+ ? suboption.half_price
886
+ : suboption.price
887
+ return {
888
+ id: suboption.id,
889
+ name: suboption.name,
890
+ position: suboption.position || 'whole',
891
+ price,
892
+ quantity: suboption.quantity,
893
+ selected: true,
894
+ total: price
895
+ }
896
+ })
897
+ } else {
898
+ states = preselectedSuboptions.map((suboption, i) => {
899
+ const price = preselectedOptions[i]?.with_half_option && suboption?.half_price && suboption?.position !== 'whole'
900
+ ? suboption.half_price
901
+ : suboption.price
902
+
903
+ return {
904
+ id: suboption.id,
905
+ name: suboption.name,
906
+ position: suboption.position || 'whole',
907
+ price,
908
+ quantity: 1,
909
+ selected: true,
910
+ total: price
911
+ }
912
+ })
913
+ }
914
+
915
+ let suboptionsArray = []
916
+ let newPizzaState = {}
917
+ preselectedOptions.map((option, i) => {
918
+ const defaultSuboption = {
919
+ option: option,
920
+ suboption: preselectedSuboptions[i],
921
+ state: states?.find((state) => state?.id === preselectedSuboptions[i]?.id)
922
+ }
923
+ suboptionsArray = [...suboptionsArray, defaultSuboption]
924
+ if (option?.with_half_option) {
925
+ newPizzaState = {
926
+ ...newPizzaState,
927
+ [`option:${option?.id}`]: {
928
+ ...newPizzaState?.[`option:${option?.id}`],
929
+ [`suboption:${preselectedSuboptions[i]?.id}`]: isAlsea
930
+ ? (states[i]?.position === 'whole' ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0)
931
+ : (states[i]?.position === 'whole' ? 1 : 0.5) * states[i].quantity
932
+ }
933
+ }
934
+ const suboptionValue = isAlsea
935
+ ? ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) + (states[i].quantity >= 2 ? 0.5 : 0))
936
+ : ((states[i]?.position === 'whole' || (option?.max === 1 && option?.min === 1) ? 1 : 0.5) * states[i].quantity)
937
+
938
+ const value = suboptionValue + (newPizzaState[`option:${option?.id}`].value || 0)
939
+ newPizzaState[`option:${option?.id}`].value = value
940
+ }
941
+ })
942
+ setPizzaState(newPizzaState)
943
+ setDefaultSubOptions(suboptionsArray)
944
+ setCustomDefaultSubOptions(suboptionsArray)
945
+ }
946
+ }, [JSON.stringify(product.product), product?.loading])
947
+
948
+ if (isStarbucks) {
949
+ useEffect(() => {
950
+ if (product?.product && Object.keys(product?.product).length) {
951
+ const options = [].concat(...product.product.extras.map(extra => extra.options.filter(
952
+ option => (
953
+ option.name === 'Tamaño' &&
954
+ option.suboptions.filter(suboption => suboption.name === 'Grande (16oz - 437ml)').length === 1
955
+ )
956
+ )))
957
+ if (!options?.length) {
958
+ return
959
+ }
960
+ const suboptions = []
961
+ .concat(...options.map(option => option.suboptions))
962
+ .filter(suboption => suboption.name === 'Grande (16oz - 437ml)')
963
+ const states = suboptions.map((suboption, i) => {
964
+ const price = options[i].with_half_option && suboption.half_price && suboption?.position !== 'whole'
965
+ ? suboption.half_price
966
+ : suboption.price
967
+
968
+ return {
969
+ id: suboption.id,
970
+ name: suboption.name,
971
+ position: suboption.position || 'whole',
972
+ price,
973
+ quantity: 1,
974
+ selected: true,
975
+ total: price
976
+ }
977
+ })
978
+ const defaultOptions = options.map((option, i) => {
979
+ return {
980
+ option: option,
981
+ suboption: suboptions[i],
982
+ state: states[i]
983
+ }
984
+ })
985
+ setDefaultSubOptions([...customDefaultSubOptions, ...defaultOptions])
986
+ }
987
+ }, [customDefaultSubOptions])
988
+ }
989
+ /**
990
+ * Check if defaultSubOption has content to set product Cart
991
+ */
992
+ useEffect(() => {
993
+ if (defaultSubOptions?.length) {
994
+ handleChangeSuboptionDefault(defaultSubOptions)
995
+ }
996
+ }, [JSON.stringify(defaultSubOptions)])
997
+
998
+ /**
999
+ * Load product on component mounted
1000
+ */
1001
+ useEffect(() => {
1002
+ if (!props.product && (!props.businessId || !props.categoryId || !props.productId)) {
1003
+ throw new Error('`businessId` && `categoryId` && `productId` are required if `product` was not provided.')
1004
+ }
1005
+ if ((props.product && props.product?.load_type === 'lazy' && props.businessId && props.categoryId && props.productId) ||
1006
+ (!props.product && props.businessId && props.categoryId && props.productId)
1007
+ ) {
1008
+ loadProductWithOptions()
1009
+ }
1010
+ return () => {
1011
+ if (requestsState.product) {
1012
+ requestsState.product.cancel()
1013
+ }
1014
+ }
1015
+ }, [])
1016
+
1017
+ useEffect(() => {
1018
+ if (!isService) return
1019
+
1020
+ if (isCartProduct) {
1021
+ getProfessionalList()
1022
+ } else {
1023
+ setProfessionalListState({
1024
+ ...professionalListState,
1025
+ professionals: professionalList
1026
+ })
1027
+ }
1028
+ }, [isService, isCartProduct, professionalList])
1029
+
1030
+ return (
1031
+ <>
1032
+ {
1033
+ UIComponent && (
1034
+ <UIComponent
1035
+ {...props}
1036
+ productObject={product}
1037
+ productCart={productCart}
1038
+ errors={errors}
1039
+ editMode={editMode}
1040
+ isSoldOut={isSoldOut}
1041
+ actionStatus={actionStatus}
1042
+ maxProductQuantity={maxProductQuantity}
1043
+ pizzaState={pizzaState}
1044
+ setPizzaState={setPizzaState}
1045
+ increment={increment}
1046
+ decrement={decrement}
1047
+ handleChangeProductCartQuantity={handleChangeProductCartQuantity}
1048
+ handleSave={handleSave}
1049
+ showOption={showOption}
1050
+ handleCreateGuestUser={handleCreateGuestUser}
1051
+ handleFavoriteProduct={handleFavoriteProduct}
1052
+ handleChangeIngredientState={handleChangeIngredientState}
1053
+ handleChangeSuboptionState={handleChangeSuboptionState}
1054
+ handleChangeCommentState={handleChangeCommentState}
1055
+ professionalListState={professionalListState}
1056
+ cart2={props.productCart}
1057
+ isAlsea={isAlsea}
1058
+ quesoYSalsaOptions={quesoYSalsaOptions}
1059
+ />
1060
+ )
1061
+ }
1062
+ </>
1063
+ )
1064
+ }
1065
+
1066
+ ProductForm.propTypes = {
1067
+ /**
1068
+ * UI Component, this must be containt all graphic elements and use parent props
1069
+ */
1070
+ UIComponent: PropTypes.elementType,
1071
+ /**
1072
+ * `businessId`
1073
+ */
1074
+ businessId: PropTypes.number.isRequired,
1075
+ /**
1076
+ * `categoryId` is required if `product` prop is not present
1077
+ */
1078
+ categoryId: PropTypes.number,
1079
+ /**
1080
+ * `productId` is required if `product` prop is not present
1081
+ */
1082
+ productId: PropTypes.number,
1083
+ /**
1084
+ * `product` is required if `businessId`, `categoryId` or `productId` is not present
1085
+ */
1086
+ product: PropTypes.object,
1087
+ /**
1088
+ * Product from cart
1089
+ */
1090
+ productCart: PropTypes.object,
1091
+ /**
1092
+ * useOrderContext
1093
+ */
1094
+ useOrderContext: PropTypes.bool,
1095
+ /**
1096
+ * Function to save event
1097
+ */
1098
+ onSave: PropTypes.func
1099
+ }
1100
+
1101
+ ProductForm.defaultProps = {
1102
+ productCart: {},
1103
+ useOrderContext: true,
1104
+ balance: 0
1105
+ }