@aglyn/plugins-commerce 1.0.0-beta.143

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 (417) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +10 -0
  3. package/package.json +57 -0
  4. package/src/index.d.ts +38 -0
  5. package/src/index.js +38 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/account.d.ts +31 -0
  8. package/src/lib/components/account.js +645 -0
  9. package/src/lib/components/account.js.map +1 -0
  10. package/src/lib/components/cart.d.ts +38 -0
  11. package/src/lib/components/cart.js +851 -0
  12. package/src/lib/components/cart.js.map +1 -0
  13. package/src/lib/components/commerce-console-page.d.ts +32 -0
  14. package/src/lib/components/commerce-console-page.js +296 -0
  15. package/src/lib/components/commerce-console-page.js.map +1 -0
  16. package/src/lib/components/commerce-console-sections.d.ts +34 -0
  17. package/src/lib/components/commerce-console-sections.js +62 -0
  18. package/src/lib/components/commerce-console-sections.js.map +1 -0
  19. package/src/lib/components/console/catalog-organization-card.component.d.ts +14 -0
  20. package/src/lib/components/console/catalog-organization-card.component.js +1087 -0
  21. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -0
  22. package/src/lib/components/console/commerce-analytics-card.component.d.ts +27 -0
  23. package/src/lib/components/console/commerce-analytics-card.component.js +310 -0
  24. package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -0
  25. package/src/lib/components/console/commerce-glance-card.component.d.ts +20 -0
  26. package/src/lib/components/console/commerce-glance-card.component.js +340 -0
  27. package/src/lib/components/console/commerce-glance-card.component.js.map +1 -0
  28. package/src/lib/components/console/commerce-stat-tile.component.d.ts +37 -0
  29. package/src/lib/components/console/commerce-stat-tile.component.js +72 -0
  30. package/src/lib/components/console/commerce-stat-tile.component.js.map +1 -0
  31. package/src/lib/components/console/discounts-card.component.d.ts +14 -0
  32. package/src/lib/components/console/discounts-card.component.js +443 -0
  33. package/src/lib/components/console/discounts-card.component.js.map +1 -0
  34. package/src/lib/components/console/entitlement-gate.component.d.ts +76 -0
  35. package/src/lib/components/console/entitlement-gate.component.js +115 -0
  36. package/src/lib/components/console/entitlement-gate.component.js.map +1 -0
  37. package/src/lib/components/console/gift-cards-card.component.d.ts +29 -0
  38. package/src/lib/components/console/gift-cards-card.component.js +379 -0
  39. package/src/lib/components/console/gift-cards-card.component.js.map +1 -0
  40. package/src/lib/components/console/host-coupons-card.component.d.ts +14 -0
  41. package/src/lib/components/console/host-coupons-card.component.js +252 -0
  42. package/src/lib/components/console/host-coupons-card.component.js.map +1 -0
  43. package/src/lib/components/console/host-orders-card.component.d.ts +13 -0
  44. package/src/lib/components/console/host-orders-card.component.js +861 -0
  45. package/src/lib/components/console/host-orders-card.component.js.map +1 -0
  46. package/src/lib/components/console/locations-card.component.d.ts +14 -0
  47. package/src/lib/components/console/locations-card.component.js +256 -0
  48. package/src/lib/components/console/locations-card.component.js.map +1 -0
  49. package/src/lib/components/console/member-posts-card.component.d.ts +8 -0
  50. package/src/lib/components/console/member-posts-card.component.js +283 -0
  51. package/src/lib/components/console/member-posts-card.component.js.map +1 -0
  52. package/src/lib/components/console/newest-site-users-card.component.d.ts +36 -0
  53. package/src/lib/components/console/newest-site-users-card.component.js +126 -0
  54. package/src/lib/components/console/newest-site-users-card.component.js.map +1 -0
  55. package/src/lib/components/console/order-detail-dialog.component.d.ts +28 -0
  56. package/src/lib/components/console/order-detail-dialog.component.js +976 -0
  57. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -0
  58. package/src/lib/components/console/paid-media.d.ts +127 -0
  59. package/src/lib/components/console/paid-media.js +397 -0
  60. package/src/lib/components/console/paid-media.js.map +1 -0
  61. package/src/lib/components/console/payments-settings-card.component.d.ts +13 -0
  62. package/src/lib/components/console/payments-settings-card.component.js +295 -0
  63. package/src/lib/components/console/payments-settings-card.component.js.map +1 -0
  64. package/src/lib/components/console/pos-page.component.d.ts +13 -0
  65. package/src/lib/components/console/pos-page.component.js +916 -0
  66. package/src/lib/components/console/pos-page.component.js.map +1 -0
  67. package/src/lib/components/console/product-editor-dialog.component.d.ts +32 -0
  68. package/src/lib/components/console/product-editor-dialog.component.js +1374 -0
  69. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -0
  70. package/src/lib/components/console/products-hub-card.component.d.ts +16 -0
  71. package/src/lib/components/console/products-hub-card.component.js +1280 -0
  72. package/src/lib/components/console/products-hub-card.component.js.map +1 -0
  73. package/src/lib/components/console/products-hub-zone.component.d.ts +50 -0
  74. package/src/lib/components/console/products-hub-zone.component.js +200 -0
  75. package/src/lib/components/console/products-hub-zone.component.js.map +1 -0
  76. package/src/lib/components/console/recovery-queue-card.component.d.ts +39 -0
  77. package/src/lib/components/console/recovery-queue-card.component.js +262 -0
  78. package/src/lib/components/console/recovery-queue-card.component.js.map +1 -0
  79. package/src/lib/components/console/registers-card.component.d.ts +21 -0
  80. package/src/lib/components/console/registers-card.component.js +296 -0
  81. package/src/lib/components/console/registers-card.component.js.map +1 -0
  82. package/src/lib/components/console/reservations-card.component.d.ts +14 -0
  83. package/src/lib/components/console/reservations-card.component.js +650 -0
  84. package/src/lib/components/console/reservations-card.component.js.map +1 -0
  85. package/src/lib/components/console/reviews-moderation-card.component.d.ts +8 -0
  86. package/src/lib/components/console/reviews-moderation-card.component.js +235 -0
  87. package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -0
  88. package/src/lib/components/console/shipping-settings-card.component.d.ts +38 -0
  89. package/src/lib/components/console/shipping-settings-card.component.js +560 -0
  90. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -0
  91. package/src/lib/components/console/stock-movements-card.component.d.ts +31 -0
  92. package/src/lib/components/console/stock-movements-card.component.js +333 -0
  93. package/src/lib/components/console/stock-movements-card.component.js.map +1 -0
  94. package/src/lib/components/console/store-settings-card.component.d.ts +14 -0
  95. package/src/lib/components/console/store-settings-card.component.js +300 -0
  96. package/src/lib/components/console/store-settings-card.component.js.map +1 -0
  97. package/src/lib/components/console/storefront-tax-summary-card.component.d.ts +73 -0
  98. package/src/lib/components/console/storefront-tax-summary-card.component.js +408 -0
  99. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -0
  100. package/src/lib/components/console/suppliers-card.component.d.ts +8 -0
  101. package/src/lib/components/console/suppliers-card.component.js +328 -0
  102. package/src/lib/components/console/suppliers-card.component.js.map +1 -0
  103. package/src/lib/components/console/tax-settings-card.component.d.ts +14 -0
  104. package/src/lib/components/console/tax-settings-card.component.js +473 -0
  105. package/src/lib/components/console/tax-settings-card.component.js.map +1 -0
  106. package/src/lib/components/gate.d.ts +39 -0
  107. package/src/lib/components/gate.js +202 -0
  108. package/src/lib/components/gate.js.map +1 -0
  109. package/src/lib/components/gated-video.d.ts +40 -0
  110. package/src/lib/components/gated-video.js +366 -0
  111. package/src/lib/components/gated-video.js.map +1 -0
  112. package/src/lib/components/member-feed.d.ts +32 -0
  113. package/src/lib/components/member-feed.js +198 -0
  114. package/src/lib/components/member-feed.js.map +1 -0
  115. package/src/lib/components/member-recovery.d.ts +35 -0
  116. package/src/lib/components/member-recovery.js +313 -0
  117. package/src/lib/components/member-recovery.js.map +1 -0
  118. package/src/lib/components/member-signin.d.ts +35 -0
  119. package/src/lib/components/member-signin.js +257 -0
  120. package/src/lib/components/member-signin.js.map +1 -0
  121. package/src/lib/components/member-signup.d.ts +34 -0
  122. package/src/lib/components/member-signup.js +287 -0
  123. package/src/lib/components/member-signup.js.map +1 -0
  124. package/src/lib/components/newsletter-signup.d.ts +39 -0
  125. package/src/lib/components/newsletter-signup.js +229 -0
  126. package/src/lib/components/newsletter-signup.js.map +1 -0
  127. package/src/lib/components/product-detail.d.ts +38 -0
  128. package/src/lib/components/product-detail.js +1022 -0
  129. package/src/lib/components/product-detail.js.map +1 -0
  130. package/src/lib/components/product-grid.d.ts +67 -0
  131. package/src/lib/components/product-grid.js +909 -0
  132. package/src/lib/components/product-grid.js.map +1 -0
  133. package/src/lib/components/product-reviews.d.ts +32 -0
  134. package/src/lib/components/product-reviews.js +349 -0
  135. package/src/lib/components/product-reviews.js.map +1 -0
  136. package/src/lib/components/related-products.d.ts +33 -0
  137. package/src/lib/components/related-products.js +232 -0
  138. package/src/lib/components/related-products.js.map +1 -0
  139. package/src/lib/components/reservation-widget.d.ts +33 -0
  140. package/src/lib/components/reservation-widget.js +418 -0
  141. package/src/lib/components/reservation-widget.js.map +1 -0
  142. package/src/lib/components/storefront-payment-element-fallback.d.ts +18 -0
  143. package/src/lib/components/storefront-payment-element-fallback.js +51 -0
  144. package/src/lib/components/storefront-payment-element-fallback.js.map +1 -0
  145. package/src/lib/components/storefront-payment-element.d.ts +78 -0
  146. package/src/lib/components/storefront-payment-element.js +156 -0
  147. package/src/lib/components/storefront-payment-element.js.map +1 -0
  148. package/src/lib/components/wishlist.d.ts +46 -0
  149. package/src/lib/components/wishlist.js +304 -0
  150. package/src/lib/components/wishlist.js.map +1 -0
  151. package/src/lib/constants/bundle-common.d.ts +18 -0
  152. package/src/lib/constants/bundle-common.js +18 -0
  153. package/src/lib/constants/bundle-common.js.map +1 -0
  154. package/src/lib/constants/product-filters.d.ts +35 -0
  155. package/src/lib/constants/product-filters.js +109 -0
  156. package/src/lib/constants/product-filters.js.map +1 -0
  157. package/src/lib/model/commerce-cart.d.ts +52 -0
  158. package/src/lib/model/commerce-cart.js +102 -0
  159. package/src/lib/model/commerce-cart.js.map +1 -0
  160. package/src/lib/model/commerce-connect-transfer.d.ts +243 -0
  161. package/src/lib/model/commerce-connect-transfer.js +231 -0
  162. package/src/lib/model/commerce-connect-transfer.js.map +1 -0
  163. package/src/lib/model/commerce-discounts.d.ts +164 -0
  164. package/src/lib/model/commerce-discounts.js +231 -0
  165. package/src/lib/model/commerce-discounts.js.map +1 -0
  166. package/src/lib/model/commerce-dispute.d.ts +124 -0
  167. package/src/lib/model/commerce-dispute.js +225 -0
  168. package/src/lib/model/commerce-dispute.js.map +1 -0
  169. package/src/lib/model/commerce-gift-cards.d.ts +107 -0
  170. package/src/lib/model/commerce-gift-cards.js +121 -0
  171. package/src/lib/model/commerce-gift-cards.js.map +1 -0
  172. package/src/lib/model/commerce-io.d.ts +36 -0
  173. package/src/lib/model/commerce-io.js +245 -0
  174. package/src/lib/model/commerce-io.js.map +1 -0
  175. package/src/lib/model/commerce-orders.d.ts +1040 -0
  176. package/src/lib/model/commerce-orders.js +1022 -0
  177. package/src/lib/model/commerce-orders.js.map +1 -0
  178. package/src/lib/model/commerce-promotions.d.ts +188 -0
  179. package/src/lib/model/commerce-promotions.js +199 -0
  180. package/src/lib/model/commerce-promotions.js.map +1 -0
  181. package/src/lib/model/commerce-reservations.d.ts +175 -0
  182. package/src/lib/model/commerce-reservations.js +157 -0
  183. package/src/lib/model/commerce-reservations.js.map +1 -0
  184. package/src/lib/model/commerce-shipping.d.ts +316 -0
  185. package/src/lib/model/commerce-shipping.js +484 -0
  186. package/src/lib/model/commerce-shipping.js.map +1 -0
  187. package/src/lib/model/commerce-stock-holds.d.ts +199 -0
  188. package/src/lib/model/commerce-stock-holds.js +190 -0
  189. package/src/lib/model/commerce-stock-holds.js.map +1 -0
  190. package/src/lib/model/commerce-subscription-status.d.ts +67 -0
  191. package/src/lib/model/commerce-subscription-status.js +76 -0
  192. package/src/lib/model/commerce-subscription-status.js.map +1 -0
  193. package/src/lib/model/commerce-tax-decision.d.ts +222 -0
  194. package/src/lib/model/commerce-tax-decision.js +165 -0
  195. package/src/lib/model/commerce-tax-decision.js.map +1 -0
  196. package/src/lib/model/commerce-tax.d.ts +176 -0
  197. package/src/lib/model/commerce-tax.js +92 -0
  198. package/src/lib/model/commerce-tax.js.map +1 -0
  199. package/src/lib/model/commerce.d.ts +551 -0
  200. package/src/lib/model/commerce.js +739 -0
  201. package/src/lib/model/commerce.js.map +1 -0
  202. package/src/lib/model/index.d.ts +40 -0
  203. package/src/lib/model/index.js +40 -0
  204. package/src/lib/model/index.js.map +1 -0
  205. package/src/lib/model/order-figures.d.ts +48 -0
  206. package/src/lib/model/order-figures.js +74 -0
  207. package/src/lib/model/order-figures.js.map +1 -0
  208. package/src/lib/model/plugin-permissions.d.ts +23 -0
  209. package/src/lib/model/plugin-permissions.js +34 -0
  210. package/src/lib/model/plugin-permissions.js.map +1 -0
  211. package/src/lib/model/purchase-analytics.d.ts +186 -0
  212. package/src/lib/model/purchase-analytics.js +216 -0
  213. package/src/lib/model/purchase-analytics.js.map +1 -0
  214. package/src/lib/plugin-config.d.ts +62 -0
  215. package/src/lib/plugin-config.js +78 -0
  216. package/src/lib/plugin-config.js.map +1 -0
  217. package/src/lib/plugin.d.ts +28 -0
  218. package/src/lib/plugin.js +147 -0
  219. package/src/lib/plugin.js.map +1 -0
  220. package/src/lib/server/billing-webhook.d.ts +26 -0
  221. package/src/lib/server/billing-webhook.js +4011 -0
  222. package/src/lib/server/billing-webhook.js.map +1 -0
  223. package/src/lib/server/cancel-order.d.ts +87 -0
  224. package/src/lib/server/cancel-order.js +368 -0
  225. package/src/lib/server/cancel-order.js.map +1 -0
  226. package/src/lib/server/cart-checkout.d.ts +25 -0
  227. package/src/lib/server/cart-checkout.js +976 -0
  228. package/src/lib/server/cart-checkout.js.map +1 -0
  229. package/src/lib/server/cart-cookie.d.ts +32 -0
  230. package/src/lib/server/cart-cookie.js +77 -0
  231. package/src/lib/server/cart-cookie.js.map +1 -0
  232. package/src/lib/server/cart.d.ts +34 -0
  233. package/src/lib/server/cart.js +181 -0
  234. package/src/lib/server/cart.js.map +1 -0
  235. package/src/lib/server/catalog.d.ts +127 -0
  236. package/src/lib/server/catalog.js +277 -0
  237. package/src/lib/server/catalog.js.map +1 -0
  238. package/src/lib/server/checkout.d.ts +36 -0
  239. package/src/lib/server/checkout.js +915 -0
  240. package/src/lib/server/checkout.js.map +1 -0
  241. package/src/lib/server/connect.d.ts +26 -0
  242. package/src/lib/server/connect.js +238 -0
  243. package/src/lib/server/connect.js.map +1 -0
  244. package/src/lib/server/contact-refund.d.ts +117 -0
  245. package/src/lib/server/contact-refund.js +196 -0
  246. package/src/lib/server/contact-refund.js.map +1 -0
  247. package/src/lib/server/download.d.ts +39 -0
  248. package/src/lib/server/download.js +206 -0
  249. package/src/lib/server/download.js.map +1 -0
  250. package/src/lib/server/draft-order.d.ts +26 -0
  251. package/src/lib/server/draft-order.js +706 -0
  252. package/src/lib/server/draft-order.js.map +1 -0
  253. package/src/lib/server/feed.d.ts +24 -0
  254. package/src/lib/server/feed.js +59 -0
  255. package/src/lib/server/feed.js.map +1 -0
  256. package/src/lib/server/fulfill-order.d.ts +97 -0
  257. package/src/lib/server/fulfill-order.js +235 -0
  258. package/src/lib/server/fulfill-order.js.map +1 -0
  259. package/src/lib/server/gate.d.ts +36 -0
  260. package/src/lib/server/gate.js +112 -0
  261. package/src/lib/server/gate.js.map +1 -0
  262. package/src/lib/server/gift-cards.d.ts +42 -0
  263. package/src/lib/server/gift-cards.js +219 -0
  264. package/src/lib/server/gift-cards.js.map +1 -0
  265. package/src/lib/server/low-stock.d.ts +49 -0
  266. package/src/lib/server/low-stock.js +60 -0
  267. package/src/lib/server/low-stock.js.map +1 -0
  268. package/src/lib/server/manual-tax-rate.d.ts +82 -0
  269. package/src/lib/server/manual-tax-rate.js +113 -0
  270. package/src/lib/server/manual-tax-rate.js.map +1 -0
  271. package/src/lib/server/member-feed.d.ts +24 -0
  272. package/src/lib/server/member-feed.js +72 -0
  273. package/src/lib/server/member-feed.js.map +1 -0
  274. package/src/lib/server/member-name-search.d.ts +37 -0
  275. package/src/lib/server/member-name-search.js +42 -0
  276. package/src/lib/server/member-name-search.js.map +1 -0
  277. package/src/lib/server/member-post.d.ts +23 -0
  278. package/src/lib/server/member-post.js +166 -0
  279. package/src/lib/server/member-post.js.map +1 -0
  280. package/src/lib/server/membership-account.d.ts +37 -0
  281. package/src/lib/server/membership-account.js +148 -0
  282. package/src/lib/server/membership-account.js.map +1 -0
  283. package/src/lib/server/membership-admin-password.d.ts +33 -0
  284. package/src/lib/server/membership-admin-password.js +202 -0
  285. package/src/lib/server/membership-admin-password.js.map +1 -0
  286. package/src/lib/server/membership-content.d.ts +23 -0
  287. package/src/lib/server/membership-content.js +105 -0
  288. package/src/lib/server/membership-content.js.map +1 -0
  289. package/src/lib/server/membership-login.d.ts +19 -0
  290. package/src/lib/server/membership-login.js +137 -0
  291. package/src/lib/server/membership-login.js.map +1 -0
  292. package/src/lib/server/membership-logout.d.ts +19 -0
  293. package/src/lib/server/membership-logout.js +35 -0
  294. package/src/lib/server/membership-logout.js.map +1 -0
  295. package/src/lib/server/membership-recover.d.ts +60 -0
  296. package/src/lib/server/membership-recover.js +244 -0
  297. package/src/lib/server/membership-recover.js.map +1 -0
  298. package/src/lib/server/membership-register.d.ts +22 -0
  299. package/src/lib/server/membership-register.js +241 -0
  300. package/src/lib/server/membership-register.js.map +1 -0
  301. package/src/lib/server/membership-reset.d.ts +26 -0
  302. package/src/lib/server/membership-reset.js +88 -0
  303. package/src/lib/server/membership-reset.js.map +1 -0
  304. package/src/lib/server/membership-wishlist.d.ts +23 -0
  305. package/src/lib/server/membership-wishlist.js +89 -0
  306. package/src/lib/server/membership-wishlist.js.map +1 -0
  307. package/src/lib/server/membership.d.ts +96 -0
  308. package/src/lib/server/membership.js +222 -0
  309. package/src/lib/server/membership.js.map +1 -0
  310. package/src/lib/server/native-checkout.d.ts +170 -0
  311. package/src/lib/server/native-checkout.js +193 -0
  312. package/src/lib/server/native-checkout.js.map +1 -0
  313. package/src/lib/server/newsletter.d.ts +23 -0
  314. package/src/lib/server/newsletter.js +323 -0
  315. package/src/lib/server/newsletter.js.map +1 -0
  316. package/src/lib/server/notify-restock.d.ts +22 -0
  317. package/src/lib/server/notify-restock.js +84 -0
  318. package/src/lib/server/notify-restock.js.map +1 -0
  319. package/src/lib/server/order-analytics.d.ts +134 -0
  320. package/src/lib/server/order-analytics.js +263 -0
  321. package/src/lib/server/order-analytics.js.map +1 -0
  322. package/src/lib/server/order-figures.d.ts +33 -0
  323. package/src/lib/server/order-figures.js +220 -0
  324. package/src/lib/server/order-figures.js.map +1 -0
  325. package/src/lib/server/paid-media-delivery.d.ts +173 -0
  326. package/src/lib/server/paid-media-delivery.js +212 -0
  327. package/src/lib/server/paid-media-delivery.js.map +1 -0
  328. package/src/lib/server/pos-order.d.ts +54 -0
  329. package/src/lib/server/pos-order.js +1137 -0
  330. package/src/lib/server/pos-order.js.map +1 -0
  331. package/src/lib/server/process-abandoned.d.ts +54 -0
  332. package/src/lib/server/process-abandoned.js +222 -0
  333. package/src/lib/server/process-abandoned.js.map +1 -0
  334. package/src/lib/server/process-restock.d.ts +42 -0
  335. package/src/lib/server/process-restock.js +219 -0
  336. package/src/lib/server/process-restock.js.map +1 -0
  337. package/src/lib/server/product-card.d.ts +29 -0
  338. package/src/lib/server/product-card.js +54 -0
  339. package/src/lib/server/product-card.js.map +1 -0
  340. package/src/lib/server/product.d.ts +70 -0
  341. package/src/lib/server/product.js +105 -0
  342. package/src/lib/server/product.js.map +1 -0
  343. package/src/lib/server/promotion-hold.d.ts +144 -0
  344. package/src/lib/server/promotion-hold.js +174 -0
  345. package/src/lib/server/promotion-hold.js.map +1 -0
  346. package/src/lib/server/reconcile-stock.d.ts +106 -0
  347. package/src/lib/server/reconcile-stock.js +349 -0
  348. package/src/lib/server/reconcile-stock.js.map +1 -0
  349. package/src/lib/server/refund.d.ts +45 -0
  350. package/src/lib/server/refund.js +607 -0
  351. package/src/lib/server/refund.js.map +1 -0
  352. package/src/lib/server/related.d.ts +24 -0
  353. package/src/lib/server/related.js +104 -0
  354. package/src/lib/server/related.js.map +1 -0
  355. package/src/lib/server/reservation-availability.d.ts +23 -0
  356. package/src/lib/server/reservation-availability.js +111 -0
  357. package/src/lib/server/reservation-availability.js.map +1 -0
  358. package/src/lib/server/reserve-stock.d.ts +156 -0
  359. package/src/lib/server/reserve-stock.js +146 -0
  360. package/src/lib/server/reserve-stock.js.map +1 -0
  361. package/src/lib/server/reserve.d.ts +25 -0
  362. package/src/lib/server/reserve.js +426 -0
  363. package/src/lib/server/reserve.js.map +1 -0
  364. package/src/lib/server/restock-flag.d.ts +202 -0
  365. package/src/lib/server/restock-flag.js +449 -0
  366. package/src/lib/server/restock-flag.js.map +1 -0
  367. package/src/lib/server/reviews.d.ts +51 -0
  368. package/src/lib/server/reviews.js +132 -0
  369. package/src/lib/server/reviews.js.map +1 -0
  370. package/src/lib/server/site-page-enricher.d.ts +42 -0
  371. package/src/lib/server/site-page-enricher.js +149 -0
  372. package/src/lib/server/site-page-enricher.js.map +1 -0
  373. package/src/lib/server/site-page-resolver.d.ts +26 -0
  374. package/src/lib/server/site-page-resolver.js +223 -0
  375. package/src/lib/server/site-page-resolver.js.map +1 -0
  376. package/src/lib/server/stock-hold.d.ts +127 -0
  377. package/src/lib/server/stock-hold.js +258 -0
  378. package/src/lib/server/stock-hold.js.map +1 -0
  379. package/src/lib/server/storefront-tax-record.d.ts +22 -0
  380. package/src/lib/server/storefront-tax-record.js +218 -0
  381. package/src/lib/server/storefront-tax-record.js.map +1 -0
  382. package/src/lib/server/storefront-tax.d.ts +197 -0
  383. package/src/lib/server/storefront-tax.js +245 -0
  384. package/src/lib/server/storefront-tax.js.map +1 -0
  385. package/src/lib/server/stream.d.ts +25 -0
  386. package/src/lib/server/stream.js +191 -0
  387. package/src/lib/server/stream.js.map +1 -0
  388. package/src/lib/server/subscription-portal.d.ts +24 -0
  389. package/src/lib/server/subscription-portal.js +91 -0
  390. package/src/lib/server/subscription-portal.js.map +1 -0
  391. package/src/lib/server/supplier-outbox.d.ts +190 -0
  392. package/src/lib/server/supplier-outbox.js +342 -0
  393. package/src/lib/server/supplier-outbox.js.map +1 -0
  394. package/src/lib/server/supplier-update.d.ts +23 -0
  395. package/src/lib/server/supplier-update.js +289 -0
  396. package/src/lib/server/supplier-update.js.map +1 -0
  397. package/src/lib/server/tax-profile.d.ts +29 -0
  398. package/src/lib/server/tax-profile.js +40 -0
  399. package/src/lib/server/tax-profile.js.map +1 -0
  400. package/src/lib/server.d.ts +32 -0
  401. package/src/lib/server.js +311 -0
  402. package/src/lib/server.js.map +1 -0
  403. package/src/lib/site.d.ts +27 -0
  404. package/src/lib/site.js +134 -0
  405. package/src/lib/site.js.map +1 -0
  406. package/src/lib/utils/escape-html.d.ts +41 -0
  407. package/src/lib/utils/escape-html.js +41 -0
  408. package/src/lib/utils/escape-html.js.map +1 -0
  409. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  410. package/src/lib/utils/generate-preset-id.js +25 -0
  411. package/src/lib/utils/generate-preset-id.js.map +1 -0
  412. package/src/lib/utils/member-continue.d.ts +45 -0
  413. package/src/lib/utils/member-continue.js +49 -0
  414. package/src/lib/utils/member-continue.js.map +1 -0
  415. package/src/lib/utils/use-storefront-purchase-event.d.ts +60 -0
  416. package/src/lib/utils/use-storefront-purchase-event.js +142 -0
  417. package/src/lib/utils/use-storefront-purchase-event.js.map +1 -0
@@ -0,0 +1,1374 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
19
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
20
+ import * as CommerceModel from "../../model/index.js";
21
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
22
+ import { Timestamp } from "@aglyn/shared-util-timestamp";
23
+ import { Alert, Autocomplete, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, MenuItem, Stack, TableBody, TableCell, TableHead, TableRow, TextField, Typography } from "@mui/material";
24
+ import { collection, doc, getDoc, setDoc } from "firebase/firestore";
25
+ import { useCallback, useMemo, useState } from "react";
26
+ import { ceilingedWindow, collectionCeiling, useFirestore, useFirestoreCollection, useHostResourceApi, writeGuardedBySeed } from "@aglyn/tenant-feature-instance";
27
+ import { useMediaPicker } from "@aglyn/aglyn";
28
+ import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
29
+ import { seoListingFieldCount, seoListingFieldTooLong } from "@aglyn/aglyn/app-utils/seo-listing-fields";
30
+ import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
31
+ import { EntitlementUpsell, useCommerceEntitlement } from "./entitlement-gate.component.js";
32
+ import { MembersVideosField, PaidDownloadAddButton, PaidMediaProtection } from "./paid-media.js";
33
+ /**
34
+ * What each picker in this dialog will offer.
35
+ *
36
+ * Every one is a chooser rather than a list, so the ceiling is the number of
37
+ * options a reader can meaningfully scan, and the walk that reaches it is
38
+ * ordered by document name — a picker that silently omits an option is worse
39
+ * than one that admits it stops, because the omission looks like the option
40
+ * not existing.
41
+ */ const CATEGORY_CEILING = 250;
42
+ const RELATED_PRODUCT_CEILING = 300;
43
+ const SUPPLIER_CEILING = 50;
44
+ /**
45
+ * The search listing fields this editor holds (AGL-2910): `product.seo`
46
+ * carries a title and a description beside its share image, and nothing
47
+ * else a listing zone may propose.
48
+ */ const PRODUCT_SEO_LISTING_FIELDS = [
49
+ 'title',
50
+ 'description'
51
+ ];
52
+ /** A product nobody has saved yet, as the editor starts one. */ function blankProduct() {
53
+ return {
54
+ name: '',
55
+ slug: '',
56
+ type: 'physical',
57
+ status: 'draft',
58
+ variants: [
59
+ {
60
+ id: 'default',
61
+ priceUsd: 0
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ /** Stable key for matching variants across matrix regenerations. */ function comboKey(options) {
67
+ return Object.entries(options != null ? options : {}).sort(([a], [b])=>a.localeCompare(b)).map(([name, value])=>`${name}:${value}`).join('|');
68
+ }
69
+ function comboLabel(options) {
70
+ const values = Object.values(options != null ? options : {});
71
+ return values.length ? values.join(' / ') : 'Default';
72
+ }
73
+ /**
74
+ * Products hub editor (AGL-279): the full catalog editor — basics,
75
+ * media, tags, options → variants matrix, per-variant pricing/stock, and
76
+ * SEO overrides. Saving denormalizes `priceUsd`/`inventory` from the
77
+ * first variant so the legacy Product block + checkout API (AGL-90) keep
78
+ * charging correctly without reading the variants array.
79
+ */ export function ProductEditorDialog(props) {
80
+ var _ref, _ref1, _current_description, _current_tags, _current_categoryIds, _current_options, _current_mediaUrls, _ref2, _ref3, _current_supplierId, _current_description1, _current_tags1, _current_mediaUrls1, _current_options1, _ref4, _subs_planLabel, _gifts_planLabel, _subs_planLabel1, _gifts_planLabel1, _current_oversellPolicy, _gifts_planLabel2, _gifts_planLabel3, _current_lowStockThreshold, _subs_planLabel2, _current_subscription_trialDays, _current_digitalFiles, _current_downloadLimit, _current_gatedVideos, _ref5, _ref6, _ref7, _current_description2, _ref8, _ref9;
81
+ var _current_seo, _current_seo1, _current_options2, _current_seo2, _current_seo3, _current_seo4, _current_seo5, _current_seo6, _current_seo7, _current_seo8, _current_seo9, _current_seo10, _current_seo11;
82
+ const { hostId, product, seedFromCache, seedUnreadable, open, onClose } = props;
83
+ /**
84
+ * Field-level entitlement gates (AGL-2080). Both of these were fully
85
+ * configurable by an unentitled org and refused only at checkout — by
86
+ * `server/checkout.ts:149` for subscriptions and `:157` /
87
+ * `cart-checkout.ts:156` for gift cards. The operator saw a saved product;
88
+ * the buyer saw the refusal.
89
+ *
90
+ * `ready && !entitled`, never `!entitled`. `checkEntitlement(undefined)`
91
+ * resolves the FREE tier rather than "unknown", so gating on the raw call
92
+ * would disable these controls at a PAYING org for the render or two its
93
+ * org doc is in flight. Erring open for that window costs nothing — the
94
+ * server enforces regardless — while erring closed is a paying customer
95
+ * told they cannot use what they bought.
96
+ */ const subs = useCommerceEntitlement(hostId, 'storefrontSubscriptions');
97
+ const gifts = useCommerceEntitlement(hostId, 'giftCards');
98
+ const subsLocked = subs.ready && !subs.entitled;
99
+ const giftsLocked = gifts.ready && !gifts.entitled;
100
+ const firestore = useFirestore();
101
+ const createHostResource = useHostResourceApi();
102
+ const { enqueueSnackbar } = useSnackbar();
103
+ /**
104
+ * The three pickers this dialog draws, read only once it is OPEN.
105
+ *
106
+ * `ProductEditorDialog` is rendered unconditionally by the products hub and
107
+ * takes `open` as a prop, so its body runs on every render of that page. A
108
+ * listener built here without the gate subscribes whether or not anyone
109
+ * opens the editor, which charges every visit to the catalog for three
110
+ * collections the visitor never sees.
111
+ *
112
+ * `null` is what `useFirestoreCollection` takes to mean "do not subscribe",
113
+ * so the closed dialog costs nothing and the open one pays once.
114
+ */ const { data: categoryDocs } = useFirestoreCollection(()=>open ? collectionCeiling(collection(firestore, 'hosts', hostId, 'productCategories'), CATEGORY_CEILING) : null, [
115
+ firestore,
116
+ hostId,
117
+ open
118
+ ], {
119
+ idField: '$id'
120
+ });
121
+ const { data: allProductDocs } = useFirestoreCollection(()=>open ? collectionCeiling(collection(firestore, 'hosts', hostId, 'products'), RELATED_PRODUCT_CEILING) : null, [
122
+ firestore,
123
+ hostId,
124
+ open
125
+ ], {
126
+ idField: '$id'
127
+ });
128
+ const { data: supplierDocs } = useFirestoreCollection(()=>open ? collectionCeiling(collection(firestore, 'hosts', hostId, 'suppliers'), SUPPLIER_CEILING) : null, [
129
+ firestore,
130
+ hostId,
131
+ open
132
+ ], {
133
+ idField: '$id'
134
+ });
135
+ const categories = useMemo(()=>ceilingedWindow(categoryDocs != null ? categoryDocs : undefined, CATEGORY_CEILING), [
136
+ categoryDocs
137
+ ]);
138
+ const relatedProducts = useMemo(()=>ceilingedWindow(allProductDocs != null ? allProductDocs : undefined, RELATED_PRODUCT_CEILING), [
139
+ allProductDocs
140
+ ]);
141
+ const suppliers = useMemo(()=>ceilingedWindow(supplierDocs != null ? supplierDocs : undefined, SUPPLIER_CEILING), [
142
+ supplierDocs
143
+ ]);
144
+ const lifted = useMemo(()=>product ? CommerceModel.liftLegacyProduct(product) : null, [
145
+ product
146
+ ]);
147
+ const [draft, setDraft] = useState(null);
148
+ const [slugTouched, setSlugTouched] = useState(false);
149
+ // The console media browser is provided by the shell (AGL-395); opening it
150
+ // resolves with the chosen asset (or null if cancelled).
151
+ const { pickMedia } = useMediaPicker();
152
+ const pick = useCallback(async (apply)=>{
153
+ const media = await (pickMedia == null ? void 0 : pickMedia());
154
+ if (media) apply(media);
155
+ }, [
156
+ pickMedia
157
+ ]);
158
+ // Lazy-init per open; parent remounts via `key` on product change.
159
+ const current = (_ref = draft != null ? draft : lifted) != null ? _ref : blankProduct();
160
+ const update = (patch)=>setDraft(_extends({}, current, patch));
161
+ /** The shell's zone renderer (AGL-2910); `null` outside the console shell. */ const WidgetSlot = useConsoleWidgetSlot();
162
+ /**
163
+ * A listing zone widget's proposal, staged into the draft like typing.
164
+ * Functional, because a proposal can arrive after a generation finishes,
165
+ * and a closure over the render that started it would put back every edit
166
+ * made while it ran. Only the fields a product's listing holds are taken.
167
+ */ const proposeSeoValues = useCallback((values)=>{
168
+ const staged = {};
169
+ for (const field of PRODUCT_SEO_LISTING_FIELDS){
170
+ const value = values[field];
171
+ if (typeof value === 'string') staged[field] = value;
172
+ }
173
+ if (!Object.keys(staged).length) return;
174
+ setDraft((prior)=>{
175
+ var _ref;
176
+ const base = (_ref = prior != null ? prior : lifted) != null ? _ref : blankProduct();
177
+ return _extends({}, base, {
178
+ seo: _extends({}, base.seo, staged)
179
+ });
180
+ });
181
+ }, [
182
+ lifted
183
+ ]);
184
+ /**
185
+ * The product zone's proposal (AGL-2916): copy staged into the draft like
186
+ * typing, functional for the reason `proposeSeoValues` gives. Option names
187
+ * go through `renameProductOptions`, so every variant keeps its price, SKU
188
+ * and stock.
189
+ */ const proposeProductValues = useCallback((values)=>{
190
+ setDraft((prior)=>{
191
+ var _ref;
192
+ const base = (_ref = prior != null ? prior : lifted) != null ? _ref : blankProduct();
193
+ return _extends({}, base, CommerceModel.productCopyPatch(base, values));
194
+ });
195
+ }, [
196
+ lifted
197
+ ]);
198
+ const zoneCategories = useMemo(()=>categories.rows.map((category)=>{
199
+ var _category_name;
200
+ return {
201
+ id: String(category.$id),
202
+ name: String((_category_name = category.name) != null ? _category_name : '')
203
+ };
204
+ }), [
205
+ categories.rows
206
+ ]);
207
+ const zoneProduct = {
208
+ id: (_ref1 = product == null ? void 0 : product.$id) != null ? _ref1 : null,
209
+ name: current.name,
210
+ type: current.type,
211
+ description: String((_current_description = current.description) != null ? _current_description : ''),
212
+ tags: (_current_tags = current.tags) != null ? _current_tags : [],
213
+ categoryIds: (_current_categoryIds = current.categoryIds) != null ? _current_categoryIds : [],
214
+ options: ((_current_options = current.options) != null ? _current_options : []).map((option)=>({
215
+ name: option.name,
216
+ values: option.values
217
+ })),
218
+ mediaUrls: (_current_mediaUrls = current.mediaUrls) != null ? _current_mediaUrls : [],
219
+ seoTitle: (_ref2 = (_current_seo = current.seo) == null ? void 0 : _current_seo.title) != null ? _ref2 : '',
220
+ seoDescription: (_ref3 = (_current_seo1 = current.seo) == null ? void 0 : _current_seo1.description) != null ? _ref3 : ''
221
+ };
222
+ const error = current.name ? CommerceModel.validateProduct(current) : null;
223
+ /**
224
+ * Stock tracking is inert on a subscription-only product (AGL-1744) —
225
+ * nothing decrements it on the first charge or on any renewal, so the
226
+ * editor stops offering a number it cannot keep true. Existing
227
+ * configuration is NOT discarded: the stored value stays on the document
228
+ * (this save is a full replace of `current`, and `current` is untouched
229
+ * here), stays visible in the disabled field, and becomes editable again
230
+ * the moment Billing goes back to "One-time purchase" or "Both".
231
+ */ const stockApplies = CommerceModel.stockTrackingApplies(current);
232
+ const strandedStock = stockApplies ? null : CommerceModel.productInventory(current);
233
+ /**
234
+ * The one destructive move, and the merchant makes it deliberately.
235
+ *
236
+ * Needed because the checkout gate is unchanged: a stored `0` on a
237
+ * subscription-only product still 409s every new subscriber, and with the
238
+ * field disabled that would be a trap with no way out. `inventoryByLocation`
239
+ * goes with it — `adjustVariantInventory` re-sums the flat count from those
240
+ * buckets (AGL-286), so leaving them would let a later restock resurrect a
241
+ * count this just cleared. Keys are DELETED rather than set undefined; the
242
+ * save is a client-direct `setDoc`, which rejects undefined values.
243
+ */ const handleClearStockTracking = ()=>{
244
+ update({
245
+ variants: current.variants.map((variant)=>{
246
+ const { inventoryByLocation: _perLocation } = variant, rest = _object_without_properties_loose(variant, [
247
+ "inventoryByLocation"
248
+ ]);
249
+ return _extends({}, rest, {
250
+ inventory: null
251
+ });
252
+ })
253
+ });
254
+ };
255
+ const handleName = (name)=>update(_extends({
256
+ name
257
+ }, !product && !slugTouched ? {
258
+ slug: CommerceModel.commerceSlug(name)
259
+ } : {}));
260
+ const handleOptionsChange = useCallback((index, patch)=>{
261
+ var _current_options;
262
+ var _current_options1;
263
+ // A RENAME moves each variant's selection to the new name (AGL-3066).
264
+ // Rebuilding the matrix matches variants by name and value, so under a
265
+ // new name it finds none and replaces every variant, its id, price, SKU
266
+ // and stock with them. Only a change of values rebuilds.
267
+ if (patch && ((_current_options1 = current.options) == null ? void 0 : _current_options1[index]) && Object.keys(patch).length === 1 && typeof patch.name === 'string') {
268
+ update(CommerceModel.renameProductOptions(current, current.options.map((option, at)=>at === index ? patch.name : option.name)));
269
+ return;
270
+ }
271
+ const options = [
272
+ ...(_current_options = current.options) != null ? _current_options : []
273
+ ];
274
+ if (patch === null) options.splice(index, 1);
275
+ else options[index] = _extends({
276
+ name: '',
277
+ values: []
278
+ }, options[index], patch);
279
+ // Regenerate the matrix, carrying data over by option-combo key so
280
+ // edits to prices/SKUs survive option tweaks.
281
+ const previous = new Map(current.variants.map((variant)=>[
282
+ comboKey(variant.options),
283
+ variant
284
+ ]));
285
+ const fallback = current.variants[0];
286
+ const variants = CommerceModel.expandVariantMatrix(options).map((combo, comboIndex)=>{
287
+ var _ref, _ref1;
288
+ const existing = previous.get(comboKey(combo));
289
+ return existing != null ? existing : {
290
+ id: `v${Date.now().toString(36)}${comboIndex}`,
291
+ options: combo,
292
+ priceUsd: (_ref = fallback == null ? void 0 : fallback.priceUsd) != null ? _ref : 0,
293
+ inventory: (_ref1 = fallback == null ? void 0 : fallback.inventory) != null ? _ref1 : null
294
+ };
295
+ });
296
+ update({
297
+ options,
298
+ variants
299
+ });
300
+ }, [
301
+ current
302
+ ]);
303
+ const handleVariantField = (index, field, raw)=>{
304
+ const variants = [
305
+ ...current.variants
306
+ ];
307
+ const numeric = [
308
+ 'priceUsd',
309
+ 'compareAtPriceUsd',
310
+ 'weightGrams'
311
+ ];
312
+ const value = field === 'inventory' ? raw.trim() === '' ? null : Math.max(0, Math.round(Number(raw))) : numeric.includes(field) ? raw.trim() === '' ? undefined : Number(raw) : raw;
313
+ variants[index] = _extends({}, variants[index], {
314
+ [field]: value
315
+ });
316
+ if (value === undefined && (field === 'compareAtPriceUsd' || field === 'weightGrams')) {
317
+ delete variants[index][field];
318
+ }
319
+ update({
320
+ variants
321
+ });
322
+ };
323
+ const handleSave = useCallback(async ()=>{
324
+ var _ref, _ref1, _ref2;
325
+ var _current_mediaUrls;
326
+ if (!current.name.trim() || error) return;
327
+ const primaryVariant = current.variants[0];
328
+ // JSON-safe base (no Firestore Timestamp — it won't survive the API
329
+ // hop); millis fields are what the checkout + Product block read.
330
+ /**
331
+ * `$id` is dropped, not carried (AGL-1374).
332
+ *
333
+ * `current` is `draft ?? liftLegacyProduct(product)`, and the hub stamps
334
+ * `$id` onto every row it lists (`{...liftLegacyProduct(p), $id: p.$id}`)
335
+ * — a SYNTHETIC key `idField: '$id'` puts on the in-memory object, which
336
+ * nothing persists. Spreading `current` carried it into the payload, and
337
+ * this write is `merge: false`, so it was stored as a real field on every
338
+ * product save. Nothing reads it; it is the listener's bookkeeping
339
+ * leaking into storage, and once there no reader can tell it from a field
340
+ * the editor meant to write. Excluding it also CLEANS the key off any
341
+ * product a previous save corrupted, because a replacing write stores
342
+ * exactly the payload.
343
+ *
344
+ * `product.$id` is still the doc path below — reading the synthetic key
345
+ * is what it is for. Writing it is the bug.
346
+ */ const seeded = current;
347
+ const { $id: _syntheticId } = seeded, currentFields = _object_without_properties_loose(seeded, [
348
+ "$id"
349
+ ]);
350
+ const base = _extends({}, currentFields, CommerceModel.productSearchFields({
351
+ name: current.name.trim().slice(0, 120),
352
+ variants: current.variants
353
+ }), {
354
+ slug: current.slug || CommerceModel.commerceSlug(current.name),
355
+ priceUsd: (_ref = primaryVariant == null ? void 0 : primaryVariant.priceUsd) != null ? _ref : 0,
356
+ inventory: CommerceModel.productInventory(current),
357
+ imageUrl: (_ref1 = (_ref2 = (_current_mediaUrls = current.mediaUrls) == null ? void 0 : _current_mediaUrls[0]) != null ? _ref2 : current.imageUrl) != null ? _ref1 : null,
358
+ updatedAtMs: Date.now()
359
+ });
360
+ try {
361
+ /**
362
+ * Refuse an EDIT whose seed the server never confirmed (AGL-1358).
363
+ *
364
+ * `merge: false` — a genuine full-document replace, and the payload is
365
+ * `{...current}`, which is `draft ?? lifted(product)`: every field the
366
+ * dialog was seeded with, whether or not it was touched. So a cached
367
+ * seed does not lose the one edit, it replaces the stored product with
368
+ * the cache's whole picture of it — price, variants, stock, media,
369
+ * status, SEO. Anything written since that snapshot, by a colleague or
370
+ * by the checkout engine, is gone.
371
+ *
372
+ * Only the edit path is guarded. A NEW product goes through the
373
+ * quota-enforcing resources API at a fresh uid and can overwrite
374
+ * nothing; the first snapshot of any listener is `fromCache: true`, so
375
+ * guarding a create would refuse a save that was never unsafe.
376
+ *
377
+ * The guard WRAPS the write: an early return is a shape you can keep
378
+ * while losing the protection.
379
+ */ const verdict = await writeGuardedBySeed({
380
+ subject: 'product',
381
+ unreadable: Boolean(product) && seedUnreadable,
382
+ fromCache: Boolean(product) && seedFromCache
383
+ }, async ()=>{
384
+ if (product) {
385
+ /**
386
+ * RESERVATIONS ARE NOT THE EDITOR'S TO WRITE (AGL-2356).
387
+ *
388
+ * Checkout stores live stock holds in a `stockHolds` map on this
389
+ * document, and this is a `merge: false` replace of a payload
390
+ * built from the dialog's SEED. Left alone it would write whatever
391
+ * that seed happened to carry, and both directions are wrong: a
392
+ * seed taken before a shopper reached checkout erases a live
393
+ * reservation and reopens the oversell for that session, and a
394
+ * seed carrying a hold that has since settled resurrects a
395
+ * reservation against stock that is already sold.
396
+ *
397
+ * So the field is dropped from the payload and re-read from the
398
+ * server immediately before the write. One extra read on a product
399
+ * save, in exchange for an editor that cannot invent or destroy a
400
+ * reservation. The AGL-1358 seed guard above bounds how stale the
401
+ * REST of this payload can be; it cannot help here, because a hold
402
+ * legitimately appears seconds after a perfectly fresh seed.
403
+ */ const productRef = doc(firestore, 'hosts', hostId, 'products', product.$id);
404
+ const { stockHolds: _seededHolds } = base, withoutHolds = _object_without_properties_loose(base, [
405
+ "stockHolds"
406
+ ]);
407
+ const liveHolds = await getDoc(productRef).then((snapshot)=>snapshot.get('stockHolds')).catch(()=>undefined);
408
+ // Edit stays client-direct (no quota consumed); full replace.
409
+ await setDoc(productRef, _extends({}, withoutHolds, liveHolds ? {
410
+ stockHolds: liveHolds
411
+ } : {}, {
412
+ updatedAt: Timestamp.now()
413
+ }), {
414
+ merge: false
415
+ });
416
+ } else {
417
+ // New product rides the quota-enforcing resources API (AGL-473) —
418
+ // it re-checks the `commerce` entitlement and productsPerHost.
419
+ await createHostResource({
420
+ hostId,
421
+ resource: 'product',
422
+ data: _extends({}, base, {
423
+ createdAtMs: Date.now()
424
+ })
425
+ });
426
+ }
427
+ });
428
+ // A refusal leaves the editor open with every field as edited. Closing
429
+ // it would discard the work AND look like a save that succeeded.
430
+ if (!verdict.ok) {
431
+ return void enqueueSnackbar(verdict.message, {
432
+ variant: 'warning',
433
+ persist: false
434
+ });
435
+ }
436
+ onClose();
437
+ enqueueSnackbar('Product saved', {
438
+ variant: 'success',
439
+ persist: false
440
+ });
441
+ } catch (saveError) {
442
+ var _ref3;
443
+ console.error(saveError);
444
+ enqueueSnackbar((_ref3 = saveError == null ? void 0 : saveError.message) != null ? _ref3 : 'An error has occurred', {
445
+ variant: 'error',
446
+ allowDuplicate: true
447
+ });
448
+ }
449
+ }, [
450
+ current,
451
+ error,
452
+ product,
453
+ seedFromCache,
454
+ seedUnreadable,
455
+ firestore,
456
+ hostId,
457
+ createHostResource,
458
+ onClose,
459
+ enqueueSnackbar
460
+ ]);
461
+ return /*#__PURE__*/ _jsxs(Dialog, {
462
+ open: open,
463
+ onClose: onClose,
464
+ maxWidth: "md",
465
+ fullWidth: true,
466
+ children: [
467
+ /*#__PURE__*/ _jsx(DialogTitle, {
468
+ children: product ? 'Edit product' : 'Add product'
469
+ }),
470
+ /*#__PURE__*/ _jsxs(DialogContent, {
471
+ sx: {
472
+ display: 'flex',
473
+ flexDirection: 'column',
474
+ gap: 2
475
+ },
476
+ children: [
477
+ /*#__PURE__*/ _jsxs(Stack, {
478
+ direction: {
479
+ xs: 'column',
480
+ sm: 'row'
481
+ },
482
+ spacing: 2,
483
+ sx: {
484
+ mt: 1
485
+ },
486
+ children: [
487
+ /*#__PURE__*/ _jsx(TextField, {
488
+ label: "Name",
489
+ value: current.name,
490
+ onChange: (event)=>handleName(event.target.value),
491
+ size: "small",
492
+ autoFocus: true,
493
+ fullWidth: true
494
+ }),
495
+ /*#__PURE__*/ _jsx(TextField, {
496
+ label: "Slug",
497
+ value: current.slug,
498
+ onChange: (event)=>{
499
+ setSlugTouched(true);
500
+ update({
501
+ slug: CommerceModel.commerceSlug(event.target.value)
502
+ });
503
+ },
504
+ size: "small",
505
+ fullWidth: true,
506
+ helperText: current.slug ? `/products/${current.slug}` : undefined
507
+ })
508
+ ]
509
+ }),
510
+ /*#__PURE__*/ _jsxs(Stack, {
511
+ direction: "row",
512
+ spacing: 2,
513
+ children: [
514
+ /*#__PURE__*/ _jsxs(TextField, {
515
+ label: "Type",
516
+ value: current.type,
517
+ onChange: (event)=>update({
518
+ type: event.target.value
519
+ }),
520
+ size: "small",
521
+ select: true,
522
+ sx: {
523
+ minWidth: 140
524
+ },
525
+ children: [
526
+ /*#__PURE__*/ _jsx(MenuItem, {
527
+ value: "physical",
528
+ children: 'Physical'
529
+ }),
530
+ /*#__PURE__*/ _jsx(MenuItem, {
531
+ value: "digital",
532
+ children: 'Digital'
533
+ }),
534
+ /*#__PURE__*/ _jsx(MenuItem, {
535
+ value: "service",
536
+ children: 'Service'
537
+ })
538
+ ]
539
+ }),
540
+ /*#__PURE__*/ _jsxs(TextField, {
541
+ label: "Status",
542
+ value: current.status,
543
+ onChange: (event)=>update({
544
+ status: event.target.value
545
+ }),
546
+ size: "small",
547
+ select: true,
548
+ sx: {
549
+ minWidth: 140
550
+ },
551
+ children: [
552
+ /*#__PURE__*/ _jsx(MenuItem, {
553
+ value: "draft",
554
+ children: 'Draft'
555
+ }),
556
+ /*#__PURE__*/ _jsx(MenuItem, {
557
+ value: "active",
558
+ children: 'Active'
559
+ }),
560
+ /*#__PURE__*/ _jsx(MenuItem, {
561
+ value: "archived",
562
+ children: 'Archived'
563
+ })
564
+ ]
565
+ }),
566
+ suppliers.rows.length > 0 ? /*#__PURE__*/ _jsxs(TextField, {
567
+ label: "Supplier",
568
+ value: (_current_supplierId = current.supplierId) != null ? _current_supplierId : '',
569
+ onChange: (event)=>update({
570
+ supplierId: event.target.value || undefined
571
+ }),
572
+ size: "small",
573
+ select: true,
574
+ sx: {
575
+ minWidth: 160
576
+ },
577
+ helperText: "Routes paid orders",
578
+ children: [
579
+ /*#__PURE__*/ _jsx(MenuItem, {
580
+ value: "",
581
+ children: 'None (self-fulfilled)'
582
+ }),
583
+ suppliers.rows.map((supplier)=>/*#__PURE__*/ _jsx(MenuItem, {
584
+ value: supplier.$id,
585
+ children: supplier.name
586
+ }, supplier.$id))
587
+ ]
588
+ }) : null
589
+ ]
590
+ }),
591
+ /*#__PURE__*/ _jsx(TextField, {
592
+ label: "Description",
593
+ value: (_current_description1 = current.description) != null ? _current_description1 : '',
594
+ onChange: (event)=>update({
595
+ description: event.target.value
596
+ }),
597
+ size: "small",
598
+ multiline: true,
599
+ minRows: 2
600
+ }),
601
+ /*#__PURE__*/ _jsx(Autocomplete, {
602
+ multiple: true,
603
+ freeSolo: true,
604
+ options: [],
605
+ value: (_current_tags1 = current.tags) != null ? _current_tags1 : [],
606
+ onChange: (_event, tags)=>update({
607
+ tags: tags.map((tag)=>String(tag).trim()).filter(Boolean)
608
+ }),
609
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
610
+ label: "Tags",
611
+ size: "small",
612
+ placeholder: "Type and press Enter"
613
+ }))
614
+ }),
615
+ categories.rows.length > 0 ? /*#__PURE__*/ _jsx(Autocomplete, {
616
+ multiple: true,
617
+ options: categories.rows,
618
+ getOptionLabel: (category)=>category.name,
619
+ isOptionEqualToValue: (option, value)=>option.$id === value.$id,
620
+ value: categories.rows.filter((category)=>{
621
+ var _current_categoryIds;
622
+ return ((_current_categoryIds = current.categoryIds) != null ? _current_categoryIds : []).includes(category.$id);
623
+ }),
624
+ onChange: (_event, picked)=>update({
625
+ categoryIds: picked.map((category)=>category.$id)
626
+ }),
627
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
628
+ label: "Categories",
629
+ size: "small"
630
+ }))
631
+ }) : null,
632
+ WidgetSlot ? /*#__PURE__*/ _jsx(WidgetSlot, {
633
+ slot: "productEditor",
634
+ hostId: hostId,
635
+ orgId: undefined,
636
+ product: zoneProduct,
637
+ categories: zoneCategories,
638
+ proposeValues: proposeProductValues
639
+ }) : null,
640
+ /*#__PURE__*/ _jsx(Divider, {
641
+ textAlign: "left",
642
+ children: 'Media'
643
+ }),
644
+ /*#__PURE__*/ _jsxs(Box, {
645
+ sx: {
646
+ display: 'flex',
647
+ gap: 1,
648
+ flexWrap: 'wrap'
649
+ },
650
+ children: [
651
+ ((_current_mediaUrls1 = current.mediaUrls) != null ? _current_mediaUrls1 : []).map((url, index)=>/*#__PURE__*/ _jsxs(Box, {
652
+ sx: {
653
+ position: 'relative'
654
+ },
655
+ children: [
656
+ /*#__PURE__*/ _jsx(Box, {
657
+ component: "img",
658
+ src: url,
659
+ alt: "",
660
+ sx: {
661
+ width: 72,
662
+ height: 72,
663
+ objectFit: 'cover',
664
+ borderRadius: 1,
665
+ border: 1,
666
+ borderColor: 'divider'
667
+ }
668
+ }),
669
+ /*#__PURE__*/ _jsx(IconButton, {
670
+ size: "small",
671
+ "aria-label": "Remove image",
672
+ onClick: ()=>{
673
+ var _current_mediaUrls;
674
+ return update({
675
+ mediaUrls: ((_current_mediaUrls = current.mediaUrls) != null ? _current_mediaUrls : []).filter((_item, itemIndex)=>itemIndex !== index)
676
+ });
677
+ },
678
+ sx: {
679
+ position: 'absolute',
680
+ top: -8,
681
+ right: -8,
682
+ bgcolor: 'background.paper',
683
+ border: 1,
684
+ borderColor: 'divider',
685
+ p: 0.25
686
+ },
687
+ children: '✕'
688
+ })
689
+ ]
690
+ }, `${url}-${index}`)),
691
+ /*#__PURE__*/ _jsx(Button, {
692
+ size: "small",
693
+ onClick: ()=>void pick((media)=>{
694
+ var _current_mediaUrls;
695
+ return update({
696
+ mediaUrls: [
697
+ ...(_current_mediaUrls = current.mediaUrls) != null ? _current_mediaUrls : [],
698
+ media.url
699
+ ]
700
+ });
701
+ }),
702
+ children: 'Add image'
703
+ })
704
+ ]
705
+ }),
706
+ /*#__PURE__*/ _jsx(Divider, {
707
+ textAlign: "left",
708
+ children: 'Options & variants'
709
+ }),
710
+ ((_current_options1 = current.options) != null ? _current_options1 : []).map((option, index)=>/*#__PURE__*/ _jsxs(Stack, {
711
+ direction: "row",
712
+ spacing: 1,
713
+ sx: {
714
+ alignItems: 'flex-start'
715
+ },
716
+ children: [
717
+ /*#__PURE__*/ _jsx(TextField, {
718
+ label: "Option",
719
+ value: option.name,
720
+ onChange: (event)=>handleOptionsChange(index, {
721
+ name: event.target.value
722
+ }),
723
+ size: "small",
724
+ sx: {
725
+ width: 160
726
+ },
727
+ placeholder: "Size"
728
+ }),
729
+ /*#__PURE__*/ _jsx(Autocomplete, {
730
+ multiple: true,
731
+ freeSolo: true,
732
+ options: [],
733
+ value: option.values,
734
+ onChange: (_event, values)=>handleOptionsChange(index, {
735
+ values: values.map((value)=>String(value).trim()).filter(Boolean)
736
+ }),
737
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
738
+ label: "Values",
739
+ size: "small",
740
+ placeholder: "S, M, L…"
741
+ })),
742
+ sx: {
743
+ flex: 1
744
+ }
745
+ }),
746
+ /*#__PURE__*/ _jsx(Button, {
747
+ size: "small",
748
+ color: "error",
749
+ onClick: ()=>handleOptionsChange(index, null),
750
+ sx: {
751
+ mt: 0.5
752
+ },
753
+ children: 'Remove'
754
+ })
755
+ ]
756
+ }, index)),
757
+ ((_ref4 = (_current_options2 = current.options) == null ? void 0 : _current_options2.length) != null ? _ref4 : 0) < CommerceModel.COMMERCE_MAX_OPTIONS ? /*#__PURE__*/ _jsx(Button, {
758
+ size: "small",
759
+ sx: {
760
+ alignSelf: 'flex-start'
761
+ },
762
+ onClick: ()=>{
763
+ var _ref;
764
+ var _current_options;
765
+ return handleOptionsChange((_ref = (_current_options = current.options) == null ? void 0 : _current_options.length) != null ? _ref : 0, {
766
+ name: '',
767
+ values: []
768
+ });
769
+ },
770
+ children: 'Add option'
771
+ }) : null,
772
+ /*#__PURE__*/ _jsx(Box, {
773
+ children: /*#__PURE__*/ _jsxs(ScrollTable, {
774
+ size: "small",
775
+ children: [
776
+ /*#__PURE__*/ _jsx(TableHead, {
777
+ children: /*#__PURE__*/ _jsxs(TableRow, {
778
+ children: [
779
+ /*#__PURE__*/ _jsx(TableCell, {
780
+ children: 'Variant'
781
+ }),
782
+ /*#__PURE__*/ _jsx(TableCell, {
783
+ children: 'Price ($)'
784
+ }),
785
+ /*#__PURE__*/ _jsx(TableCell, {
786
+ children: 'Compare-at'
787
+ }),
788
+ /*#__PURE__*/ _jsx(TableCell, {
789
+ children: 'SKU'
790
+ }),
791
+ /*#__PURE__*/ _jsx(TableCell, {
792
+ children: 'Barcode'
793
+ }),
794
+ /*#__PURE__*/ _jsx(TableCell, {
795
+ children: 'Stock'
796
+ }),
797
+ /*#__PURE__*/ _jsx(TableCell, {
798
+ children: 'Weight (g)'
799
+ })
800
+ ]
801
+ })
802
+ }),
803
+ /*#__PURE__*/ _jsx(TableBody, {
804
+ children: current.variants.map((variant, index)=>{
805
+ var _variant_priceUsd, _variant_compareAtPriceUsd, _variant_sku, _variant_barcode, _variant_inventory, _variant_weightGrams;
806
+ return /*#__PURE__*/ _jsxs(TableRow, {
807
+ children: [
808
+ /*#__PURE__*/ _jsx(TableCell, {
809
+ sx: {
810
+ whiteSpace: 'nowrap'
811
+ },
812
+ children: comboLabel(variant.options)
813
+ }),
814
+ /*#__PURE__*/ _jsx(TableCell, {
815
+ children: /*#__PURE__*/ _jsx(TextField, {
816
+ value: (_variant_priceUsd = variant.priceUsd) != null ? _variant_priceUsd : '',
817
+ onChange: (event)=>handleVariantField(index, 'priceUsd', event.target.value),
818
+ size: "small",
819
+ sx: {
820
+ width: 88
821
+ },
822
+ // An empty price is marked (AGL-2916): a proposed
823
+ // product arrives with none, and Save waits for one.
824
+ error: !CommerceModel.variantHasPrice(variant),
825
+ placeholder: "Set",
826
+ slotProps: {
827
+ htmlInput: {
828
+ inputMode: 'decimal'
829
+ }
830
+ }
831
+ })
832
+ }),
833
+ /*#__PURE__*/ _jsx(TableCell, {
834
+ children: /*#__PURE__*/ _jsx(TextField, {
835
+ value: (_variant_compareAtPriceUsd = variant.compareAtPriceUsd) != null ? _variant_compareAtPriceUsd : '',
836
+ onChange: (event)=>handleVariantField(index, 'compareAtPriceUsd', event.target.value),
837
+ size: "small",
838
+ sx: {
839
+ width: 88
840
+ },
841
+ slotProps: {
842
+ htmlInput: {
843
+ inputMode: 'decimal'
844
+ }
845
+ }
846
+ })
847
+ }),
848
+ /*#__PURE__*/ _jsx(TableCell, {
849
+ children: /*#__PURE__*/ _jsx(TextField, {
850
+ value: (_variant_sku = variant.sku) != null ? _variant_sku : '',
851
+ onChange: (event)=>handleVariantField(index, 'sku', event.target.value),
852
+ size: "small",
853
+ sx: {
854
+ width: 110
855
+ }
856
+ })
857
+ }),
858
+ /*#__PURE__*/ _jsx(TableCell, {
859
+ children: /*#__PURE__*/ _jsx(TextField, {
860
+ value: (_variant_barcode = variant.barcode) != null ? _variant_barcode : '',
861
+ onChange: (event)=>handleVariantField(index, 'barcode', event.target.value),
862
+ size: "small",
863
+ sx: {
864
+ width: 110
865
+ }
866
+ })
867
+ }),
868
+ /*#__PURE__*/ _jsx(TableCell, {
869
+ children: /*#__PURE__*/ _jsx(TextField, {
870
+ value: (_variant_inventory = variant.inventory) != null ? _variant_inventory : '',
871
+ placeholder: stockApplies ? '—' : 'n/a',
872
+ disabled: !stockApplies,
873
+ onChange: (event)=>handleVariantField(index, 'inventory', event.target.value),
874
+ size: "small",
875
+ sx: {
876
+ width: 72
877
+ },
878
+ slotProps: {
879
+ htmlInput: {
880
+ inputMode: 'numeric',
881
+ 'aria-label': `Stock — ${comboLabel(variant.options)}`
882
+ }
883
+ }
884
+ })
885
+ }),
886
+ /*#__PURE__*/ _jsx(TableCell, {
887
+ children: /*#__PURE__*/ _jsx(TextField, {
888
+ value: (_variant_weightGrams = variant.weightGrams) != null ? _variant_weightGrams : '',
889
+ onChange: (event)=>handleVariantField(index, 'weightGrams', event.target.value),
890
+ size: "small",
891
+ sx: {
892
+ width: 88
893
+ },
894
+ slotProps: {
895
+ htmlInput: {
896
+ inputMode: 'numeric'
897
+ }
898
+ }
899
+ })
900
+ })
901
+ ]
902
+ }, variant.id);
903
+ })
904
+ })
905
+ ]
906
+ })
907
+ }),
908
+ /*#__PURE__*/ _jsx(Typography, {
909
+ variant: "caption",
910
+ color: "text.secondary",
911
+ children: stockApplies ? 'Blank stock = untracked; 0 shows sold out. The first variant’s ' + 'price feeds legacy Product blocks.' : 'Stock is not tracked on a digital or service subscription — ' + 'nothing decrements it, on the first charge or on any renewal. ' + 'A physical subscription decrements per cycle, and “Both — ' + 'buyer chooses” tracks the one-time sales. The first variant’s ' + 'price feeds legacy Product blocks.'
912
+ }),
913
+ strandedStock != null ? /*#__PURE__*/ _jsx(Alert, {
914
+ severity: "warning",
915
+ action: /*#__PURE__*/ _jsx(Button, {
916
+ size: "small",
917
+ onClick: handleClearStockTracking,
918
+ children: 'Clear stock'
919
+ }),
920
+ children: `This product still has stock set (${strandedStock}), but a ` + 'digital or service subscription never decrements it — not the ' + 'first charge, not any renewal — so the number will not change ' + 'on its own and does not cap subscribers. It is kept, not ' + 'deleted: a stored 0 still blocks new subscribers, and the ' + 'value becomes editable again if you switch Billing back to ' + 'one-time or “Both”, or the Type to physical.'
921
+ }) : null,
922
+ subsLocked || giftsLocked ? /*#__PURE__*/ _jsx(EntitlementUpsell, {
923
+ planLabel: undefined,
924
+ upgradeHref: subsLocked ? subs.upgradeHref : gifts.upgradeHref,
925
+ children: subsLocked && giftsLocked ? `Subscription products (${(_subs_planLabel = subs.planLabel) != null ? _subs_planLabel : 'higher plan'}) ` + `and gift cards (${(_gifts_planLabel = gifts.planLabel) != null ? _gifts_planLabel : 'higher plan'}) are ` + 'not on your plan. You can still save this product — those ' + 'two options stay locked until you upgrade.' : subsLocked ? `Subscription products are on ${(_subs_planLabel1 = subs.planLabel) != null ? _subs_planLabel1 : 'a higher plan'}. ` + 'A subscription saved without it is refused at checkout, ' + 'so the option stays locked here instead.' : `Gift cards are on ${(_gifts_planLabel1 = gifts.planLabel) != null ? _gifts_planLabel1 : 'a higher plan'}. ` + 'A gift-card product saved without it is refused at ' + 'checkout, so the option stays locked here instead.'
926
+ }) : null,
927
+ /*#__PURE__*/ _jsxs(Stack, {
928
+ direction: "row",
929
+ spacing: 2,
930
+ children: [
931
+ /*#__PURE__*/ _jsxs(TextField, {
932
+ label: "When out of stock",
933
+ value: (_current_oversellPolicy = current.oversellPolicy) != null ? _current_oversellPolicy : 'deny',
934
+ onChange: (event)=>update({
935
+ oversellPolicy: event.target.value
936
+ }),
937
+ size: "small",
938
+ select: true,
939
+ sx: {
940
+ minWidth: 200
941
+ },
942
+ children: [
943
+ /*#__PURE__*/ _jsx(MenuItem, {
944
+ value: "deny",
945
+ children: 'Stop selling (sold out)'
946
+ }),
947
+ /*#__PURE__*/ _jsx(MenuItem, {
948
+ value: "backorder",
949
+ children: 'Keep selling (backorder)'
950
+ })
951
+ ]
952
+ }),
953
+ /*#__PURE__*/ _jsxs(TextField, {
954
+ label: "Kind",
955
+ value: current.giftCard ? 'gift' : 'standard',
956
+ onChange: (event)=>update({
957
+ giftCard: event.target.value === 'gift'
958
+ }),
959
+ size: "small",
960
+ select: true,
961
+ sx: {
962
+ minWidth: 130
963
+ },
964
+ helperText: giftsLocked ? `Gift cards are on ${(_gifts_planLabel2 = gifts.planLabel) != null ? _gifts_planLabel2 : 'a higher plan'}` : 'Gift cards issue a code',
965
+ children: [
966
+ /*#__PURE__*/ _jsx(MenuItem, {
967
+ value: "standard",
968
+ children: 'Standard'
969
+ }),
970
+ /*#__PURE__*/ _jsx(MenuItem, {
971
+ value: "gift",
972
+ disabled: giftsLocked,
973
+ children: giftsLocked ? `Gift card — ${(_gifts_planLabel3 = gifts.planLabel) != null ? _gifts_planLabel3 : 'upgrade'} plan` : 'Gift card'
974
+ })
975
+ ]
976
+ }),
977
+ /*#__PURE__*/ _jsxs(TextField, {
978
+ label: "Tax",
979
+ value: current.taxExempt ? 'exempt' : 'taxable',
980
+ onChange: (event)=>update({
981
+ taxExempt: event.target.value === 'exempt'
982
+ }),
983
+ size: "small",
984
+ select: true,
985
+ sx: {
986
+ minWidth: 120
987
+ },
988
+ children: [
989
+ /*#__PURE__*/ _jsx(MenuItem, {
990
+ value: "taxable",
991
+ children: 'Taxable'
992
+ }),
993
+ /*#__PURE__*/ _jsx(MenuItem, {
994
+ value: "exempt",
995
+ children: 'Tax exempt'
996
+ })
997
+ ]
998
+ }),
999
+ /*#__PURE__*/ _jsx(TextField, {
1000
+ label: "Low-stock alert at",
1001
+ value: (_current_lowStockThreshold = current.lowStockThreshold) != null ? _current_lowStockThreshold : '',
1002
+ placeholder: "Off",
1003
+ disabled: !stockApplies,
1004
+ onChange: (event)=>{
1005
+ const raw = event.target.value.trim();
1006
+ update({
1007
+ lowStockThreshold: raw === '' ? undefined : Math.max(0, Math.round(Number(raw)))
1008
+ });
1009
+ },
1010
+ size: "small",
1011
+ sx: {
1012
+ width: 140
1013
+ },
1014
+ slotProps: {
1015
+ htmlInput: {
1016
+ inputMode: 'numeric'
1017
+ }
1018
+ },
1019
+ helperText: "Notifies managers"
1020
+ })
1021
+ ]
1022
+ }),
1023
+ /*#__PURE__*/ _jsxs(Stack, {
1024
+ direction: "row",
1025
+ spacing: 2,
1026
+ children: [
1027
+ /*#__PURE__*/ _jsxs(TextField, {
1028
+ label: "Billing",
1029
+ value: current.subscription ? current.subscriptionOptional ? 'both' : current.subscription.interval : 'once',
1030
+ onChange: (event)=>{
1031
+ const value = event.target.value;
1032
+ // "Both" (AGL-545): the PDP offers one-time OR subscribe at
1033
+ // the same price; the interval field beside picks the cadence.
1034
+ // Cleared keys are deleted (not set undefined) so the
1035
+ // client-direct setDoc on save never sees undefined values.
1036
+ const next = _extends({}, current);
1037
+ if (value === 'once') {
1038
+ delete next.subscription;
1039
+ } else {
1040
+ var _current_subscription, _ref;
1041
+ var _current_subscription1;
1042
+ next.subscription = _extends({}, (_current_subscription = current.subscription) != null ? _current_subscription : {}, {
1043
+ interval: value === 'both' ? (_ref = (_current_subscription1 = current.subscription) == null ? void 0 : _current_subscription1.interval) != null ? _ref : 'month' : value
1044
+ });
1045
+ }
1046
+ if (value === 'both') next.subscriptionOptional = true;
1047
+ else delete next.subscriptionOptional;
1048
+ // setDraft directly: update() spreads the patch over
1049
+ // `current`, which would resurrect the deleted keys.
1050
+ setDraft(next);
1051
+ },
1052
+ size: "small",
1053
+ select: true,
1054
+ sx: {
1055
+ minWidth: 180
1056
+ },
1057
+ helperText: subsLocked ? `Subscriptions are on ${(_subs_planLabel2 = subs.planLabel) != null ? _subs_planLabel2 : 'a higher plan'}` : 'Subscriptions bill until canceled',
1058
+ children: [
1059
+ /*#__PURE__*/ _jsx(MenuItem, {
1060
+ value: "once",
1061
+ children: 'One-time purchase'
1062
+ }),
1063
+ /*#__PURE__*/ _jsx(MenuItem, {
1064
+ value: "month",
1065
+ disabled: subsLocked,
1066
+ children: 'Monthly subscription'
1067
+ }),
1068
+ /*#__PURE__*/ _jsx(MenuItem, {
1069
+ value: "year",
1070
+ disabled: subsLocked,
1071
+ children: 'Yearly subscription'
1072
+ }),
1073
+ /*#__PURE__*/ _jsx(MenuItem, {
1074
+ value: "both",
1075
+ disabled: subsLocked,
1076
+ children: 'Both — buyer chooses'
1077
+ })
1078
+ ]
1079
+ }),
1080
+ current.subscription && current.subscriptionOptional ? /*#__PURE__*/ _jsxs(TextField, {
1081
+ label: "Interval",
1082
+ value: current.subscription.interval,
1083
+ onChange: (event)=>update({
1084
+ subscription: _extends({}, current.subscription, {
1085
+ interval: event.target.value
1086
+ })
1087
+ }),
1088
+ size: "small",
1089
+ select: true,
1090
+ sx: {
1091
+ minWidth: 120
1092
+ },
1093
+ children: [
1094
+ /*#__PURE__*/ _jsx(MenuItem, {
1095
+ value: "month",
1096
+ children: 'Monthly'
1097
+ }),
1098
+ /*#__PURE__*/ _jsx(MenuItem, {
1099
+ value: "year",
1100
+ children: 'Yearly'
1101
+ })
1102
+ ]
1103
+ }) : null,
1104
+ current.subscription ? /*#__PURE__*/ _jsx(TextField, {
1105
+ label: "Free trial (days)",
1106
+ placeholder: "None",
1107
+ value: (_current_subscription_trialDays = current.subscription.trialDays) != null ? _current_subscription_trialDays : '',
1108
+ onChange: (event)=>{
1109
+ const raw = event.target.value.trim();
1110
+ update({
1111
+ subscription: _extends({}, current.subscription, raw === '' ? {
1112
+ trialDays: undefined
1113
+ } : {
1114
+ trialDays: Math.max(1, Math.round(Number(raw)))
1115
+ })
1116
+ });
1117
+ },
1118
+ size: "small",
1119
+ sx: {
1120
+ width: 140
1121
+ },
1122
+ slotProps: {
1123
+ htmlInput: {
1124
+ inputMode: 'numeric'
1125
+ }
1126
+ }
1127
+ }) : null
1128
+ ]
1129
+ }),
1130
+ current.type === 'digital' ? /*#__PURE__*/ _jsxs(_Fragment, {
1131
+ children: [
1132
+ /*#__PURE__*/ _jsx(Divider, {
1133
+ textAlign: "left",
1134
+ children: 'Digital delivery'
1135
+ }),
1136
+ ((_current_digitalFiles = current.digitalFiles) != null ? _current_digitalFiles : []).map((file, index)=>{
1137
+ var _file_version;
1138
+ return /*#__PURE__*/ _jsxs(Stack, {
1139
+ direction: "row",
1140
+ spacing: 1,
1141
+ sx: {
1142
+ alignItems: 'center'
1143
+ },
1144
+ children: [
1145
+ /*#__PURE__*/ _jsxs(Typography, {
1146
+ variant: "body2",
1147
+ sx: {
1148
+ flex: 1
1149
+ },
1150
+ noWrap: true,
1151
+ children: [
1152
+ file.fileName,
1153
+ file.version ? ` · v${file.version}` : ''
1154
+ ]
1155
+ }),
1156
+ /*#__PURE__*/ _jsx(PaidMediaProtection, {
1157
+ url: file.url,
1158
+ hostId: hostId,
1159
+ productId: product == null ? void 0 : product.$id,
1160
+ kind: "download"
1161
+ }),
1162
+ /*#__PURE__*/ _jsx(TextField, {
1163
+ label: "Version",
1164
+ value: (_file_version = file.version) != null ? _file_version : '',
1165
+ onChange: (event)=>{
1166
+ var _current_digitalFiles;
1167
+ const digitalFiles = [
1168
+ ...(_current_digitalFiles = current.digitalFiles) != null ? _current_digitalFiles : []
1169
+ ];
1170
+ digitalFiles[index] = _extends({}, file, {
1171
+ version: event.target.value.slice(0, 20)
1172
+ });
1173
+ update({
1174
+ digitalFiles
1175
+ });
1176
+ },
1177
+ size: "small",
1178
+ sx: {
1179
+ width: 100
1180
+ }
1181
+ }),
1182
+ /*#__PURE__*/ _jsx(Button, {
1183
+ size: "small",
1184
+ color: "error",
1185
+ onClick: ()=>{
1186
+ var _current_digitalFiles;
1187
+ return update({
1188
+ digitalFiles: ((_current_digitalFiles = current.digitalFiles) != null ? _current_digitalFiles : []).filter((_item, itemIndex)=>itemIndex !== index)
1189
+ });
1190
+ },
1191
+ children: '✕'
1192
+ })
1193
+ ]
1194
+ }, index);
1195
+ }),
1196
+ /*#__PURE__*/ _jsxs(Stack, {
1197
+ direction: "row",
1198
+ spacing: 2,
1199
+ children: [
1200
+ /*#__PURE__*/ _jsx(PaidDownloadAddButton, {
1201
+ hostId: hostId,
1202
+ productId: product == null ? void 0 : product.$id,
1203
+ onAdd: (file)=>{
1204
+ var _current_digitalFiles;
1205
+ return update({
1206
+ digitalFiles: [
1207
+ ...(_current_digitalFiles = current.digitalFiles) != null ? _current_digitalFiles : [],
1208
+ file
1209
+ ]
1210
+ });
1211
+ }
1212
+ }),
1213
+ /*#__PURE__*/ _jsx(TextField, {
1214
+ label: "Download limit",
1215
+ placeholder: "Unlimited",
1216
+ value: (_current_downloadLimit = current.downloadLimit) != null ? _current_downloadLimit : '',
1217
+ onChange: (event)=>{
1218
+ const raw = event.target.value.trim();
1219
+ update({
1220
+ downloadLimit: raw === '' ? undefined : Math.max(1, Math.round(Number(raw)))
1221
+ });
1222
+ },
1223
+ size: "small",
1224
+ sx: {
1225
+ width: 140
1226
+ },
1227
+ slotProps: {
1228
+ htmlInput: {
1229
+ inputMode: 'numeric'
1230
+ }
1231
+ },
1232
+ helperText: "Attempts per order"
1233
+ })
1234
+ ]
1235
+ }),
1236
+ /*#__PURE__*/ _jsx(Typography, {
1237
+ variant: "caption",
1238
+ color: "text.secondary",
1239
+ children: 'Buyers always download the current files — uploading a new ' + 'version re-delivers to everyone.'
1240
+ }),
1241
+ /*#__PURE__*/ _jsx(MembersVideosField, {
1242
+ hostId: hostId,
1243
+ productId: product == null ? void 0 : product.$id,
1244
+ videos: (_current_gatedVideos = current.gatedVideos) != null ? _current_gatedVideos : [],
1245
+ onChange: (gatedVideos)=>update({
1246
+ gatedVideos
1247
+ })
1248
+ })
1249
+ ]
1250
+ }) : null,
1251
+ /*#__PURE__*/ _jsx(Autocomplete, {
1252
+ multiple: true,
1253
+ options: relatedProducts.rows.filter((item)=>!item.deletedAt && item.$id !== (product == null ? void 0 : product.$id)),
1254
+ getOptionLabel: (item)=>{
1255
+ var _item_name;
1256
+ return (_item_name = item.name) != null ? _item_name : item.$id;
1257
+ },
1258
+ isOptionEqualToValue: (option, value)=>option.$id === value.$id,
1259
+ value: relatedProducts.rows.filter((item)=>{
1260
+ var _current_relatedProductIds;
1261
+ return ((_current_relatedProductIds = current.relatedProductIds) != null ? _current_relatedProductIds : []).includes(item.$id);
1262
+ }),
1263
+ onChange: (_event, picked)=>update({
1264
+ relatedProductIds: picked.map((item)=>item.$id)
1265
+ }),
1266
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
1267
+ label: "Related products (upsells)",
1268
+ size: "small",
1269
+ helperText: "Shown by the Related products block; blank falls back to frequently-bought-together"
1270
+ }))
1271
+ }),
1272
+ /*#__PURE__*/ _jsx(Divider, {
1273
+ textAlign: "left",
1274
+ children: 'Search engine listing'
1275
+ }),
1276
+ /*#__PURE__*/ _jsxs(Stack, {
1277
+ direction: {
1278
+ xs: 'column',
1279
+ sm: 'row'
1280
+ },
1281
+ spacing: 2,
1282
+ children: [
1283
+ /*#__PURE__*/ _jsx(TextField, {
1284
+ label: "SEO title",
1285
+ value: (_ref5 = (_current_seo2 = current.seo) == null ? void 0 : _current_seo2.title) != null ? _ref5 : '',
1286
+ onChange: (event)=>update({
1287
+ seo: _extends({}, current.seo, {
1288
+ title: event.target.value
1289
+ })
1290
+ }),
1291
+ // The counts every search listing editor prints (AGL-2910): what a
1292
+ // search result shows before it truncates.
1293
+ helperText: seoListingFieldCount('title', (_current_seo3 = current.seo) == null ? void 0 : _current_seo3.title),
1294
+ error: seoListingFieldTooLong('title', (_current_seo4 = current.seo) == null ? void 0 : _current_seo4.title),
1295
+ size: "small",
1296
+ fullWidth: true
1297
+ }),
1298
+ /*#__PURE__*/ _jsx(Button, {
1299
+ size: "small",
1300
+ onClick: ()=>void pick((media)=>update({
1301
+ seo: _extends({}, current.seo, {
1302
+ imageUrl: media.url
1303
+ })
1304
+ })),
1305
+ children: ((_current_seo5 = current.seo) == null ? void 0 : _current_seo5.imageUrl) ? 'Change OG image' : 'OG image'
1306
+ })
1307
+ ]
1308
+ }),
1309
+ /*#__PURE__*/ _jsx(TextField, {
1310
+ label: "SEO description",
1311
+ value: (_ref6 = (_current_seo6 = current.seo) == null ? void 0 : _current_seo6.description) != null ? _ref6 : '',
1312
+ onChange: (event)=>update({
1313
+ seo: _extends({}, current.seo, {
1314
+ description: event.target.value
1315
+ })
1316
+ }),
1317
+ helperText: seoListingFieldCount('description', (_current_seo7 = current.seo) == null ? void 0 : _current_seo7.description),
1318
+ error: seoListingFieldTooLong('description', (_current_seo8 = current.seo) == null ? void 0 : _current_seo8.description),
1319
+ size: "small",
1320
+ multiline: true,
1321
+ minRows: 2
1322
+ }),
1323
+ WidgetSlot ? /*#__PURE__*/ _jsx(WidgetSlot, {
1324
+ slot: "seoFields",
1325
+ hostId: hostId,
1326
+ orgId: undefined,
1327
+ orgSlug: "",
1328
+ subject: {
1329
+ kind: 'product',
1330
+ id: (_ref7 = product == null ? void 0 : product.$id) != null ? _ref7 : null,
1331
+ name: current.name,
1332
+ description: String((_current_description2 = current.description) != null ? _current_description2 : '')
1333
+ },
1334
+ fields: PRODUCT_SEO_LISTING_FIELDS,
1335
+ values: {
1336
+ title: (_ref8 = (_current_seo9 = current.seo) == null ? void 0 : _current_seo9.title) != null ? _ref8 : '',
1337
+ description: (_ref9 = (_current_seo10 = current.seo) == null ? void 0 : _current_seo10.description) != null ? _ref9 : ''
1338
+ },
1339
+ hasImage: Boolean((_current_seo11 = current.seo) == null ? void 0 : _current_seo11.imageUrl),
1340
+ proposeValues: proposeSeoValues
1341
+ }) : null,
1342
+ error ? /*#__PURE__*/ _jsx(Alert, {
1343
+ severity: "warning",
1344
+ children: error
1345
+ }) : null,
1346
+ (product == null ? void 0 : product.$id) ? /*#__PURE__*/ _jsx(Typography, {
1347
+ variant: "caption",
1348
+ color: "text.secondary",
1349
+ children: `id: ${product.$id}`
1350
+ }) : null
1351
+ ]
1352
+ }),
1353
+ /*#__PURE__*/ _jsxs(DialogActions, {
1354
+ children: [
1355
+ /*#__PURE__*/ _jsx(Button, {
1356
+ onClick: onClose,
1357
+ children: 'Cancel'
1358
+ }),
1359
+ /*#__PURE__*/ _jsx(Button, {
1360
+ variant: "contained",
1361
+ color: "primary",
1362
+ disabled: !current.name.trim() || Boolean(error),
1363
+ onClick: handleSave,
1364
+ children: 'Save product'
1365
+ })
1366
+ ]
1367
+ })
1368
+ ]
1369
+ });
1370
+ }
1371
+ ProductEditorDialog.displayName = 'ProductEditorDialog';
1372
+ export default ProductEditorDialog;
1373
+
1374
+ //# sourceMappingURL=product-editor-dialog.component.js.map