@mohasinac/appkit 3.0.4 → 3.1.0

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 (320) hide show
  1. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +42 -0
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +86 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.d.ts +14 -0
  4. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +46 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +28 -0
  6. package/dist/_internal/client/features/lottery/LotteryDetailView.js +17 -0
  7. package/dist/_internal/client/features/lottery/LotteryEntriesView.d.ts +23 -0
  8. package/dist/_internal/client/features/lottery/LotteryEntriesView.js +58 -0
  9. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +21 -0
  10. package/dist/_internal/client/features/lottery/LotteryListView.js +25 -0
  11. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +19 -0
  12. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -0
  13. package/dist/_internal/client/features/lottery/LotterySlotGrid.d.ts +13 -0
  14. package/dist/_internal/client/features/lottery/LotterySlotGrid.js +21 -0
  15. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.d.ts +28 -0
  16. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +18 -0
  17. package/dist/_internal/server/features/auctions/__tests__/service.test.d.ts +1 -0
  18. package/dist/_internal/server/features/auctions/__tests__/service.test.js +140 -0
  19. package/dist/_internal/server/features/auctions/data.d.ts +2 -4
  20. package/dist/_internal/server/features/auctions/data.js +3 -9
  21. package/dist/_internal/server/features/checkout/__tests__/actions.test.d.ts +1 -0
  22. package/dist/_internal/server/features/checkout/__tests__/actions.test.js +600 -0
  23. package/dist/_internal/server/features/classified/adapters.d.ts +2 -15
  24. package/dist/_internal/server/features/classified/adapters.js +2 -17
  25. package/dist/_internal/server/features/classified/data.d.ts +1 -6
  26. package/dist/_internal/server/features/classified/data.js +2 -10
  27. package/dist/_internal/server/features/digital-code/adapters.d.ts +2 -15
  28. package/dist/_internal/server/features/digital-code/adapters.js +2 -17
  29. package/dist/_internal/server/features/digital-code/data.d.ts +1 -6
  30. package/dist/_internal/server/features/digital-code/data.js +2 -10
  31. package/dist/_internal/server/features/live/adapters.d.ts +2 -15
  32. package/dist/_internal/server/features/live/adapters.js +2 -17
  33. package/dist/_internal/server/features/live/data.d.ts +1 -6
  34. package/dist/_internal/server/features/live/data.js +2 -10
  35. package/dist/_internal/server/features/lottery/actions.d.ts +43 -0
  36. package/dist/_internal/server/features/lottery/actions.js +243 -0
  37. package/dist/_internal/server/features/lottery/adapters.d.ts +9 -0
  38. package/dist/_internal/server/features/lottery/adapters.js +21 -0
  39. package/dist/_internal/server/features/lottery/data.d.ts +20 -0
  40. package/dist/_internal/server/features/lottery/data.js +41 -0
  41. package/dist/_internal/server/features/lottery/metadata.d.ts +6 -0
  42. package/dist/_internal/server/features/lottery/metadata.js +31 -0
  43. package/dist/_internal/server/features/pre-orders/data.d.ts +2 -4
  44. package/dist/_internal/server/features/pre-orders/data.js +3 -9
  45. package/dist/_internal/server/features/prize-draws/actions.d.ts +2 -0
  46. package/dist/_internal/server/features/prize-draws/actions.js +22 -0
  47. package/dist/_internal/server/features/prize-draws/data.d.ts +1 -17
  48. package/dist/_internal/server/features/prize-draws/data.js +2 -20
  49. package/dist/_internal/server/features/prize-draws/index.d.ts +3 -1
  50. package/dist/_internal/server/features/prize-draws/index.js +2 -0
  51. package/dist/_internal/server/features/prize-draws/service.d.ts +3 -0
  52. package/dist/_internal/server/features/prize-draws/service.js +19 -0
  53. package/dist/_internal/server/features/refunds/__tests__/actions.test.d.ts +1 -0
  54. package/dist/_internal/server/features/refunds/__tests__/actions.test.js +263 -0
  55. package/dist/_internal/server/features/shared/listing-adapters.d.ts +19 -0
  56. package/dist/_internal/server/features/shared/listing-adapters.js +20 -0
  57. package/dist/_internal/server/features/shared/listing-data-factory.d.ts +9 -0
  58. package/dist/_internal/server/features/shared/listing-data-factory.js +33 -0
  59. package/dist/_internal/server/features/stores/data.d.ts +3 -14
  60. package/dist/_internal/server/features/stores/data.js +6 -44
  61. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.d.ts +1 -0
  62. package/dist/_internal/server/jobs/core/__tests__/auctionSettlement.test.js +249 -0
  63. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.d.ts +1 -0
  64. package/dist/_internal/server/jobs/core/__tests__/onBidPlaced.test.js +201 -0
  65. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.d.ts +1 -0
  66. package/dist/_internal/server/jobs/core/__tests__/onOrderCreate.test.js +169 -0
  67. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.d.ts +1 -0
  68. package/dist/_internal/server/jobs/core/__tests__/onOrderStatusChange.test.js +148 -0
  69. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.d.ts +1 -0
  70. package/dist/_internal/server/jobs/core/__tests__/onProductStockChange.test.js +210 -0
  71. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.d.ts +1 -0
  72. package/dist/_internal/server/jobs/core/__tests__/onReviewWrite.test.js +190 -0
  73. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.d.ts +1 -0
  74. package/dist/_internal/server/jobs/core/__tests__/onUserBanChange.test.js +161 -0
  75. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.d.ts +1 -0
  76. package/dist/_internal/server/jobs/core/__tests__/payoutBatch.test.js +302 -0
  77. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.d.ts +1 -0
  78. package/dist/_internal/server/jobs/core/__tests__/triggerEventRaffle.test.js +252 -0
  79. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.d.ts +1 -0
  80. package/dist/_internal/server/jobs/core/__tests__/wrapJobHandler.test.js +129 -0
  81. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  82. package/dist/_internal/shared/actions/action-registry.js +46 -0
  83. package/dist/_internal/shared/features/cart/schema.d.ts +8 -8
  84. package/dist/_internal/shared/features/cart/schema.js +3 -0
  85. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  86. package/dist/_internal/shared/listing-types/_registry.js +2 -0
  87. package/dist/_internal/shared/listing-types/auction/schema.d.ts +3 -1
  88. package/dist/_internal/shared/listing-types/auction/schema.js +7 -1
  89. package/dist/_internal/shared/listing-types/classified/schema.d.ts +2 -1
  90. package/dist/_internal/shared/listing-types/classified/schema.js +3 -1
  91. package/dist/_internal/shared/listing-types/digital-code/schema.d.ts +3 -1
  92. package/dist/_internal/shared/listing-types/digital-code/schema.js +7 -1
  93. package/dist/_internal/shared/listing-types/live/schema.d.ts +2 -1
  94. package/dist/_internal/shared/listing-types/live/schema.js +3 -1
  95. package/dist/_internal/shared/listing-types/pre-order/schema.d.ts +3 -1
  96. package/dist/_internal/shared/listing-types/pre-order/schema.js +7 -1
  97. package/dist/_internal/shared/listing-types/prize-draw/schema.d.ts +3 -1
  98. package/dist/_internal/shared/listing-types/prize-draw/schema.js +13 -1
  99. package/dist/_internal/shared/listing-types/standard/schema.d.ts +2 -1
  100. package/dist/_internal/shared/listing-types/standard/schema.js +4 -1
  101. package/dist/_internal/shared/types/base-document.d.ts +15 -0
  102. package/dist/_internal/shared/types/base-document.js +1 -0
  103. package/dist/_internal/shared/types/index.d.ts +2 -0
  104. package/dist/_internal/shared/types/index.js +1 -0
  105. package/dist/client.d.ts +13 -4
  106. package/dist/client.js +11 -2
  107. package/dist/constants/api-endpoints.d.ts +12 -0
  108. package/dist/constants/api-endpoints.js +4 -0
  109. package/dist/constants/field-names.d.ts +5 -1
  110. package/dist/constants/field-names.js +5 -1
  111. package/dist/features/addresses/repository/__tests__/addresses.repository.test.d.ts +1 -0
  112. package/dist/features/addresses/repository/__tests__/addresses.repository.test.js +386 -0
  113. package/dist/features/addresses/schemas/firestore.d.ts +2 -4
  114. package/dist/features/admin/api/bids/route.d.ts +2 -2
  115. package/dist/features/admin/api/coupons/route.d.ts +2 -2
  116. package/dist/features/admin/api/products/route.d.ts +2 -2
  117. package/dist/features/admin/api/reviews/route.d.ts +2 -2
  118. package/dist/features/admin/components/AdminFulfillmentView.d.ts +6 -0
  119. package/dist/features/admin/components/AdminFulfillmentView.js +107 -0
  120. package/dist/features/admin/components/AdminProductEditorView.js +21 -9
  121. package/dist/features/admin/components/AdminProductsView.js +3 -0
  122. package/dist/features/admin/components/DataListingView.d.ts +1 -0
  123. package/dist/features/admin/components/index.d.ts +2 -0
  124. package/dist/features/admin/components/index.js +1 -0
  125. package/dist/features/admin/repository/__tests__/notification.repository.test.d.ts +1 -0
  126. package/dist/features/admin/repository/__tests__/notification.repository.test.js +356 -0
  127. package/dist/features/admin/repository/__tests__/site-settings.repository.test.d.ts +1 -0
  128. package/dist/features/admin/repository/__tests__/site-settings.repository.test.js +298 -0
  129. package/dist/features/admin/schemas/firestore.d.ts +3 -8
  130. package/dist/features/auctions/repository/__tests__/bid.repository.test.d.ts +1 -0
  131. package/dist/features/auctions/repository/__tests__/bid.repository.test.js +290 -0
  132. package/dist/features/auctions/schemas/firestore.d.ts +2 -4
  133. package/dist/features/auctions/schemas/index.d.ts +2 -2
  134. package/dist/features/auth/api/route.d.ts +2 -2
  135. package/dist/features/auth/repository/__tests__/session.repository.test.d.ts +1 -0
  136. package/dist/features/auth/repository/__tests__/session.repository.test.js +310 -0
  137. package/dist/features/auth/repository/__tests__/session.test.d.ts +1 -0
  138. package/dist/features/auth/repository/__tests__/session.test.js +146 -0
  139. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.d.ts +1 -0
  140. package/dist/features/auth/repository/__tests__/sms-counter.repository.test.js +171 -0
  141. package/dist/features/auth/repository/__tests__/sms-counter.test.d.ts +1 -0
  142. package/dist/features/auth/repository/__tests__/sms-counter.test.js +88 -0
  143. package/dist/features/auth/repository/__tests__/token.repository.test.d.ts +1 -0
  144. package/dist/features/auth/repository/__tests__/token.repository.test.js +260 -0
  145. package/dist/features/auth/repository/__tests__/token.test.d.ts +1 -0
  146. package/dist/features/auth/repository/__tests__/token.test.js +140 -0
  147. package/dist/features/auth/repository/__tests__/user.repository.test.d.ts +1 -0
  148. package/dist/features/auth/repository/__tests__/user.repository.test.js +352 -0
  149. package/dist/features/auth/repository/__tests__/user.test.d.ts +1 -0
  150. package/dist/features/auth/repository/__tests__/user.test.js +226 -0
  151. package/dist/features/auth/repository/session.repository.js +10 -1
  152. package/dist/features/before-after/schemas/index.d.ts +2 -2
  153. package/dist/features/blog/schemas/firestore.d.ts +2 -4
  154. package/dist/features/blog/schemas/index.d.ts +4 -4
  155. package/dist/features/cart/repository/__tests__/cart.repository.test.d.ts +1 -0
  156. package/dist/features/cart/repository/__tests__/cart.repository.test.js +371 -0
  157. package/dist/features/cart/repository/cart.repository.js +2 -2
  158. package/dist/features/cart/schemas/firestore.d.ts +2 -4
  159. package/dist/features/categories/api/[id]/route.d.ts +4 -4
  160. package/dist/features/categories/api/route.d.ts +2 -2
  161. package/dist/features/categories/repository/__tests__/categories.repository.test.d.ts +1 -0
  162. package/dist/features/categories/repository/__tests__/categories.repository.test.js +813 -0
  163. package/dist/features/categories/schemas/firestore.d.ts +2 -4
  164. package/dist/features/categories/schemas/index.d.ts +4 -4
  165. package/dist/features/events/components/EventCard.js +1 -0
  166. package/dist/features/events/repository/__tests__/event.repository.test.d.ts +1 -0
  167. package/dist/features/events/repository/__tests__/event.repository.test.js +299 -0
  168. package/dist/features/events/schemas/firestore.d.ts +5 -4
  169. package/dist/features/events/schemas/index.d.ts +836 -16
  170. package/dist/features/events/schemas/index.js +80 -1
  171. package/dist/features/events/types/index.d.ts +4 -1
  172. package/dist/features/faq/repository/__tests__/faqs.repository.test.d.ts +1 -0
  173. package/dist/features/faq/repository/__tests__/faqs.repository.test.js +311 -0
  174. package/dist/features/faq/schemas/firestore.d.ts +2 -4
  175. package/dist/features/faq/schemas/index.d.ts +2 -2
  176. package/dist/features/grouped/schemas/index.d.ts +2 -2
  177. package/dist/features/history/repository/__tests__/user-history.repository.test.d.ts +1 -0
  178. package/dist/features/history/repository/__tests__/user-history.repository.test.js +180 -0
  179. package/dist/features/homepage/api/[id]/route.d.ts +4 -4
  180. package/dist/features/homepage/api/carousel/[id]/route.d.ts +4 -4
  181. package/dist/features/homepage/api/carousel/route.d.ts +2 -2
  182. package/dist/features/homepage/api/route.d.ts +2 -2
  183. package/dist/features/homepage/repository/__tests__/carousel.repository.test.d.ts +1 -0
  184. package/dist/features/homepage/repository/__tests__/carousel.repository.test.js +182 -0
  185. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.d.ts +1 -0
  186. package/dist/features/homepage/repository/__tests__/homepage-sections.repository.test.js +185 -0
  187. package/dist/features/homepage/schemas/firestore.d.ts +4 -12
  188. package/dist/features/homepage/schemas/index.d.ts +2 -2
  189. package/dist/features/lottery/repository/lottery-entry.repository.d.ts +33 -0
  190. package/dist/features/lottery/repository/lottery-entry.repository.js +130 -0
  191. package/dist/features/lottery/schemas/firestore.d.ts +49 -0
  192. package/dist/features/lottery/schemas/firestore.js +25 -0
  193. package/dist/features/lottery/schemas/zod.d.ts +113 -0
  194. package/dist/features/lottery/schemas/zod.js +39 -0
  195. package/dist/features/lottery/types/index.d.ts +67 -0
  196. package/dist/features/lottery/types/index.js +55 -0
  197. package/dist/features/messages/repository/__tests__/conversations.repository.test.d.ts +1 -0
  198. package/dist/features/messages/repository/__tests__/conversations.repository.test.js +257 -0
  199. package/dist/features/messages/schemas/firestore.d.ts +2 -4
  200. package/dist/features/messages/schemas/index.d.ts +2 -2
  201. package/dist/features/orders/repository/__tests__/orders.repository.test.d.ts +1 -0
  202. package/dist/features/orders/repository/__tests__/orders.repository.test.js +211 -0
  203. package/dist/features/orders/repository/orders.repository.d.ts +5 -0
  204. package/dist/features/orders/repository/orders.repository.js +26 -0
  205. package/dist/features/orders/schemas/firestore.d.ts +9 -5
  206. package/dist/features/orders/schemas/firestore.js +3 -0
  207. package/dist/features/payments/repository/__tests__/payout.repository.test.d.ts +1 -0
  208. package/dist/features/payments/repository/__tests__/payout.repository.test.js +292 -0
  209. package/dist/features/payments/schemas/firestore.d.ts +2 -4
  210. package/dist/features/pre-orders/schemas/index.d.ts +2 -2
  211. package/dist/features/products/api/[id]/route.d.ts +4 -4
  212. package/dist/features/products/api/route.d.ts +2 -2
  213. package/dist/features/products/constants/action-defs.d.ts +16 -0
  214. package/dist/features/products/constants/action-defs.js +16 -0
  215. package/dist/features/products/constants/listing-tabs.d.ts +48 -0
  216. package/dist/features/products/constants/listing-tabs.js +12 -0
  217. package/dist/features/products/repository/__tests__/product-features.repository.test.d.ts +1 -0
  218. package/dist/features/products/repository/__tests__/product-features.repository.test.js +329 -0
  219. package/dist/features/products/repository/__tests__/products.repository.test.d.ts +1 -0
  220. package/dist/features/products/repository/__tests__/products.repository.test.js +607 -0
  221. package/dist/features/products/repository/products.repository.d.ts +37 -0
  222. package/dist/features/products/repository/products.repository.js +28 -1
  223. package/dist/features/products/schemas/firestore.d.ts +23 -7
  224. package/dist/features/products/schemas/firestore.js +2 -0
  225. package/dist/features/products/schemas/index.d.ts +18 -18
  226. package/dist/features/products/schemas/index.js +4 -2
  227. package/dist/features/products/types/index.d.ts +3 -1
  228. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.d.ts +1 -0
  229. package/dist/features/promotions/repository/__tests__/claimed-coupons.repository.test.js +219 -0
  230. package/dist/features/promotions/repository/__tests__/coupons.repository.test.d.ts +1 -0
  231. package/dist/features/promotions/repository/__tests__/coupons.repository.test.js +480 -0
  232. package/dist/features/promotions/schemas/firestore.d.ts +2 -4
  233. package/dist/features/promotions/schemas/index.d.ts +4 -4
  234. package/dist/features/reviews/api/[id]/route.d.ts +4 -4
  235. package/dist/features/reviews/api/route.d.ts +2 -2
  236. package/dist/features/reviews/repository/__tests__/reviews.repository.test.d.ts +1 -0
  237. package/dist/features/reviews/repository/__tests__/reviews.repository.test.js +327 -0
  238. package/dist/features/reviews/schemas/firestore.d.ts +2 -4
  239. package/dist/features/reviews/schemas/index.d.ts +2 -2
  240. package/dist/features/scams/repository/__tests__/scammer.repository.test.d.ts +1 -0
  241. package/dist/features/scams/repository/__tests__/scammer.repository.test.js +385 -0
  242. package/dist/features/scams/schemas/firestore.d.ts +2 -4
  243. package/dist/features/scams/schemas/index.d.ts +4 -4
  244. package/dist/features/search/repository/__tests__/search.repository.test.d.ts +1 -0
  245. package/dist/features/search/repository/__tests__/search.repository.test.js +369 -0
  246. package/dist/features/search/schemas/index.d.ts +2 -2
  247. package/dist/features/seller/api/coupons/route.d.ts +2 -2
  248. package/dist/features/seller/api/offers/route.d.ts +2 -2
  249. package/dist/features/seller/api/products/route.d.ts +2 -2
  250. package/dist/features/seller/api/store/route.d.ts +2 -2
  251. package/dist/features/seller/components/BarcodeField.d.ts +17 -0
  252. package/dist/features/seller/components/BarcodeField.js +30 -0
  253. package/dist/features/seller/components/FulfillmentView.d.ts +5 -0
  254. package/dist/features/seller/components/FulfillmentView.js +120 -0
  255. package/dist/features/seller/components/PrintCenterView.d.ts +3 -1
  256. package/dist/features/seller/components/PrintCenterView.js +52 -4
  257. package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
  258. package/dist/features/seller/components/SellerProductShell.js +2 -1
  259. package/dist/features/seller/components/index.d.ts +4 -0
  260. package/dist/features/seller/components/index.js +2 -0
  261. package/dist/features/seller/repository/__tests__/offer.repository.test.d.ts +1 -0
  262. package/dist/features/seller/repository/__tests__/offer.repository.test.js +458 -0
  263. package/dist/features/seller/schemas/index.d.ts +2 -2
  264. package/dist/features/store-extensions/schemas/firestore.d.ts +12 -44
  265. package/dist/features/stores/components/StoreClassifiedsListing.d.ts +5 -0
  266. package/dist/features/stores/components/StoreClassifiedsListing.js +48 -0
  267. package/dist/features/stores/components/StoreClassifiedsPageView.d.ts +4 -0
  268. package/dist/features/stores/components/StoreClassifiedsPageView.js +20 -0
  269. package/dist/features/stores/components/StoreDetailLayoutView.js +23 -2
  270. package/dist/features/stores/components/StoreDigitalCodesListing.d.ts +5 -0
  271. package/dist/features/stores/components/StoreDigitalCodesListing.js +48 -0
  272. package/dist/features/stores/components/StoreDigitalCodesPageView.d.ts +4 -0
  273. package/dist/features/stores/components/StoreDigitalCodesPageView.js +20 -0
  274. package/dist/features/stores/components/StoreLiveItemsListing.d.ts +5 -0
  275. package/dist/features/stores/components/StoreLiveItemsListing.js +48 -0
  276. package/dist/features/stores/components/StoreLiveItemsPageView.d.ts +4 -0
  277. package/dist/features/stores/components/StoreLiveItemsPageView.js +20 -0
  278. package/dist/features/stores/components/index.d.ts +9 -0
  279. package/dist/features/stores/components/index.js +3 -0
  280. package/dist/features/stores/repository/__tests__/store.repository.test.d.ts +1 -0
  281. package/dist/features/stores/repository/__tests__/store.repository.test.js +261 -0
  282. package/dist/features/stores/schemas/firestore.d.ts +2 -4
  283. package/dist/features/stores/schemas/index.d.ts +4 -4
  284. package/dist/features/support/repository/__tests__/support.repository.test.d.ts +1 -0
  285. package/dist/features/support/repository/__tests__/support.repository.test.js +312 -0
  286. package/dist/features/support/schemas/firestore.d.ts +2 -4
  287. package/dist/features/support/schemas/index.d.ts +2 -2
  288. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.d.ts +1 -0
  289. package/dist/features/wishlist/repository/__tests__/user-wishlist.repository.test.js +139 -0
  290. package/dist/index.d.ts +14 -0
  291. package/dist/index.js +17 -0
  292. package/dist/next/api/routeHandler.d.ts +1 -1
  293. package/dist/next/routing/route-map.d.ts +24 -0
  294. package/dist/next/routing/route-map.js +11 -0
  295. package/dist/repositories/index.d.ts +1 -0
  296. package/dist/repositories/index.js +2 -0
  297. package/dist/schemas/registry.d.ts +24 -24
  298. package/dist/security/index.d.ts +1 -1
  299. package/dist/security/index.js +1 -1
  300. package/dist/security/pii-schemas.d.ts +2 -0
  301. package/dist/security/pii-schemas.js +6 -0
  302. package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
  303. package/dist/seed/events-seed-data.js +85 -2
  304. package/dist/seed/grouped-listings-seed-data.d.ts +5 -2
  305. package/dist/seed/index.d.ts +1 -0
  306. package/dist/seed/index.js +1 -0
  307. package/dist/seed/lottery-entries-seed-data.d.ts +2 -0
  308. package/dist/seed/lottery-entries-seed-data.js +108 -0
  309. package/dist/seed/manifest.js +1 -0
  310. package/dist/seed/products-preorders-seed-data.d.ts +5 -2
  311. package/dist/seed/products-standard-seed-data.js +8 -0
  312. package/dist/server-entry.d.ts +3 -0
  313. package/dist/server-entry.js +3 -0
  314. package/dist/server.d.ts +10 -4
  315. package/dist/server.js +6 -0
  316. package/dist/styles.css +1 -1
  317. package/dist/tailwind-utilities.css +1 -1
  318. package/dist/utils/id-generators.d.ts +8 -0
  319. package/dist/utils/id-generators.js +14 -0
  320. package/package.json +2 -1
@@ -0,0 +1,386 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { makeMockDb, makeSnap, makeQuerySnap } from "../../../../../tests/helpers/mock-firestore";
3
+ import { DatabaseError } from "../../../../errors";
4
+ const { db, mockDocRef, mockCollection, mockQuery, mockBatch } = makeMockDb();
5
+ const { mockEncryptPiiFields, mockDecryptPiiFields } = vi.hoisted(() => ({
6
+ mockEncryptPiiFields: vi.fn((d) => d),
7
+ mockDecryptPiiFields: vi.fn((d) => d),
8
+ }));
9
+ const { mockGetFirestoreCount } = vi.hoisted(() => ({
10
+ mockGetFirestoreCount: vi.fn().mockResolvedValue(0),
11
+ }));
12
+ vi.mock("../../../../providers/db-firebase/admin", () => ({
13
+ getAdminDb: () => db,
14
+ }));
15
+ vi.mock("../../../../providers/db-firebase", async (importOriginal) => {
16
+ const actual = await importOriginal();
17
+ return {
18
+ ...actual,
19
+ prepareForFirestore: (d) => d,
20
+ getFirestoreCount: mockGetFirestoreCount,
21
+ };
22
+ });
23
+ vi.mock("../../../../security", () => ({
24
+ encryptPiiFields: mockEncryptPiiFields,
25
+ decryptPiiFields: mockDecryptPiiFields,
26
+ ADDRESS_PII_FIELDS: ["fullName", "phone", "addressLine1"],
27
+ }));
28
+ vi.mock("../../../../errors/normalize", () => ({
29
+ normalizeError: vi.fn(),
30
+ }));
31
+ vi.mock("../../../../monitoring", () => ({
32
+ serverLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
33
+ }));
34
+ import { AddressesRepository } from "../addresses.repository";
35
+ const repo = new AddressesRepository();
36
+ function makeAddressDoc(overrides = {}) {
37
+ return {
38
+ id: "addr-1",
39
+ ownerType: "user",
40
+ ownerId: "user-1",
41
+ label: "Home",
42
+ fullName: "Ravi Kumar",
43
+ phone: "+91-9876543210",
44
+ addressLine1: "123 Main St",
45
+ city: "Mumbai",
46
+ state: "Maharashtra",
47
+ postalCode: "400001",
48
+ country: "IN",
49
+ isDefault: false,
50
+ createdAt: new Date(),
51
+ updatedAt: new Date(),
52
+ ...overrides,
53
+ };
54
+ }
55
+ function makeAddressInput(overrides = {}) {
56
+ return {
57
+ label: "Home",
58
+ fullName: "Ravi Kumar",
59
+ phone: "+91-9876543210",
60
+ addressLine1: "123 Main St",
61
+ city: "Mumbai",
62
+ state: "Maharashtra",
63
+ postalCode: "400001",
64
+ country: "IN",
65
+ isDefault: false,
66
+ ...overrides,
67
+ };
68
+ }
69
+ beforeEach(() => {
70
+ vi.clearAllMocks();
71
+ // Restore passthrough encryption mocks after clearAllMocks
72
+ mockEncryptPiiFields.mockImplementation((d) => d);
73
+ mockDecryptPiiFields.mockImplementation((d) => d);
74
+ db.collection.mockReturnValue(mockCollection);
75
+ mockCollection.doc.mockReturnValue(mockDocRef);
76
+ mockCollection.where.mockReturnValue(mockQuery);
77
+ mockQuery.where.mockReturnValue(mockQuery);
78
+ mockQuery.orderBy.mockReturnValue(mockQuery);
79
+ mockQuery.limit.mockReturnValue(mockQuery);
80
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
81
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
82
+ mockDocRef.set.mockResolvedValue(undefined);
83
+ mockDocRef.update.mockResolvedValue(undefined);
84
+ mockDocRef.delete.mockResolvedValue(undefined);
85
+ db.batch.mockReturnValue(mockBatch);
86
+ mockGetFirestoreCount.mockResolvedValue(0);
87
+ });
88
+ // ---------------------------------------------------------------------------
89
+ // listByOwner
90
+ // ---------------------------------------------------------------------------
91
+ describe("AddressesRepository.listByOwner", () => {
92
+ it("returns addresses for the owner", async () => {
93
+ const addr = makeAddressDoc();
94
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: addr.id, data: addr }]));
95
+ const results = await repo.listByOwner("user", "user-1");
96
+ expect(results).toHaveLength(1);
97
+ });
98
+ it("queries with ownerType AND ownerId filters", async () => {
99
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
100
+ await repo.listByOwner("user", "user-1");
101
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
102
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "user-1");
103
+ });
104
+ it("orders results by createdAt descending", async () => {
105
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
106
+ await repo.listByOwner("user", "user-1");
107
+ expect(mockQuery.orderBy).toHaveBeenCalledWith("createdAt", "desc");
108
+ });
109
+ it("returns empty array when no addresses found", async () => {
110
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
111
+ const results = await repo.listByOwner("user", "nobody");
112
+ expect(results).toHaveLength(0);
113
+ });
114
+ it("returns store addresses when ownerType=store", async () => {
115
+ const addr = makeAddressDoc({ ownerType: "store", ownerId: "store-1" });
116
+ mockQuery.get.mockResolvedValue(makeQuerySnap([{ id: addr.id, data: addr }]));
117
+ const results = await repo.listByOwner("store", "store-1");
118
+ expect(results).toHaveLength(1);
119
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "store");
120
+ });
121
+ });
122
+ // ---------------------------------------------------------------------------
123
+ // listByOwnerType
124
+ // ---------------------------------------------------------------------------
125
+ describe("AddressesRepository.listByOwnerType", () => {
126
+ it("queries only by ownerType (no ownerId filter)", async () => {
127
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
128
+ await repo.listByOwnerType("user");
129
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
130
+ // Ensure ownerId filter NOT applied (only one where call at collection level)
131
+ const collectionWhereCalls = mockCollection.where.mock.calls.filter((c) => c[0] === "ownerId");
132
+ expect(collectionWhereCalls).toHaveLength(0);
133
+ });
134
+ it("applies the limit (default 500)", async () => {
135
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
136
+ await repo.listByOwnerType("store");
137
+ expect(mockQuery.limit).toHaveBeenCalledWith(500);
138
+ });
139
+ it("applies custom limit when provided", async () => {
140
+ mockQuery.get.mockResolvedValue(makeQuerySnap([]));
141
+ await repo.listByOwnerType("store", 100);
142
+ expect(mockQuery.limit).toHaveBeenCalledWith(100);
143
+ });
144
+ });
145
+ // ---------------------------------------------------------------------------
146
+ // countByOwner
147
+ // ---------------------------------------------------------------------------
148
+ describe("AddressesRepository.countByOwner", () => {
149
+ it("returns count from getFirestoreCount", async () => {
150
+ mockGetFirestoreCount.mockResolvedValue(3);
151
+ const count = await repo.countByOwner("user", "user-1");
152
+ expect(count).toBe(3);
153
+ });
154
+ it("queries by ownerType AND ownerId", async () => {
155
+ await repo.countByOwner("user", "user-1");
156
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "user");
157
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "user-1");
158
+ });
159
+ it("returns 0 when no addresses exist", async () => {
160
+ mockGetFirestoreCount.mockResolvedValue(0);
161
+ const count = await repo.countByOwner("user", "ghost-user");
162
+ expect(count).toBe(0);
163
+ });
164
+ });
165
+ // ---------------------------------------------------------------------------
166
+ // createForOwner
167
+ // ---------------------------------------------------------------------------
168
+ describe("AddressesRepository.createForOwner", () => {
169
+ it("isDefault:false — no batch operations, docRef.set called", async () => {
170
+ const address = makeAddressDoc();
171
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
172
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: false }));
173
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
174
+ expect(mockBatch.update).not.toHaveBeenCalled();
175
+ expect(mockBatch.commit).not.toHaveBeenCalled();
176
+ });
177
+ it("isDefault:true with no existing defaults — no batch commit, docRef.set still called", async () => {
178
+ // clearDefaultFlag: empty snap → early return without batch
179
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
180
+ const address = makeAddressDoc({ isDefault: true });
181
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
182
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: true }));
183
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
184
+ expect(mockBatch.commit).not.toHaveBeenCalled();
185
+ });
186
+ it("isDefault:true with existing defaults — clears them via batch before creating", async () => {
187
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
188
+ const existingSnap = makeSnap(existingDefault, "addr-old");
189
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
190
+ const newAddress = makeAddressDoc({ isDefault: true });
191
+ mockDocRef.get.mockResolvedValue(makeSnap(newAddress, "mock-doc-id"));
192
+ await repo.createForOwner("user", "user-1", makeAddressInput({ isDefault: true }));
193
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
194
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
195
+ expect(mockDocRef.set).toHaveBeenCalledOnce();
196
+ });
197
+ it("sets ownerType and ownerId on the written document", async () => {
198
+ const address = makeAddressDoc();
199
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
200
+ await repo.createForOwner("store", "store-abc", makeAddressInput());
201
+ expect(mockDocRef.set).toHaveBeenCalledWith(expect.objectContaining({ ownerType: "store", ownerId: "store-abc" }));
202
+ });
203
+ it("re-fetches the address after write to return consistent data", async () => {
204
+ const address = makeAddressDoc();
205
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
206
+ const result = await repo.createForOwner("user", "user-1", makeAddressInput());
207
+ // The re-fetch (findById) uses mockDocRef.get — result reflects that
208
+ expect(result).toBeDefined();
209
+ expect(mockDocRef.get).toHaveBeenCalled();
210
+ });
211
+ it("throws DatabaseError when re-fetch returns null (doc not readable after write)", async () => {
212
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
213
+ await expect(repo.createForOwner("user", "user-1", makeAddressInput())).rejects.toThrow(DatabaseError);
214
+ });
215
+ it("calls encryptPiiFields with the address data on write", async () => {
216
+ const address = makeAddressDoc();
217
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
218
+ await repo.createForOwner("user", "user-1", makeAddressInput({ fullName: "Jane Doe" }));
219
+ expect(mockEncryptPiiFields).toHaveBeenCalledWith(expect.objectContaining({ fullName: "Jane Doe" }), expect.arrayContaining(["fullName", "phone", "addressLine1"]));
220
+ });
221
+ it("uses an auto-generated Firestore ID (getCollection().doc() called without args)", async () => {
222
+ const address = makeAddressDoc();
223
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "mock-doc-id"));
224
+ await repo.createForOwner("user", "user-1", makeAddressInput());
225
+ // doc() called with no argument = auto-ID
226
+ const callsWithNoArg = mockCollection.doc.mock.calls.filter((args) => args.length === 0);
227
+ expect(callsWithNoArg).not.toHaveLength(0);
228
+ });
229
+ });
230
+ // ---------------------------------------------------------------------------
231
+ // updateForOwner
232
+ // ---------------------------------------------------------------------------
233
+ describe("AddressesRepository.updateForOwner", () => {
234
+ it("throws DatabaseError when address not found", async () => {
235
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
236
+ await expect(repo.updateForOwner("user", "user-1", "addr-x", { label: "Work" })).rejects.toThrow(DatabaseError);
237
+ });
238
+ it("throws DatabaseError when ownerType does not match", async () => {
239
+ const address = makeAddressDoc({ ownerType: "store", ownerId: "store-1" });
240
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
241
+ await expect(repo.updateForOwner("user", "user-1", "addr-1", { label: "Work" })).rejects.toThrow(DatabaseError);
242
+ });
243
+ it("throws DatabaseError when ownerId does not match", async () => {
244
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
245
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
246
+ await expect(repo.updateForOwner("user", "user-1", "addr-1", { label: "Work" })).rejects.toThrow(DatabaseError);
247
+ });
248
+ it("updates the address when ownership matches", async () => {
249
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
250
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
251
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
252
+ await repo.updateForOwner("user", "user-1", "addr-1", { label: "Office" });
253
+ expect(mockDocRef.update).toHaveBeenCalled();
254
+ });
255
+ it("isDefault:true — clears existing defaults before updating", async () => {
256
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
257
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
258
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
259
+ const existingSnap = makeSnap(existingDefault, "addr-old");
260
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
261
+ await repo.updateForOwner("user", "user-1", "addr-1", { isDefault: true });
262
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
263
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
264
+ });
265
+ it("isDefault:false — does NOT clear existing defaults", async () => {
266
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
267
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
268
+ await repo.updateForOwner("user", "user-1", "addr-1", { isDefault: false });
269
+ expect(mockBatch.commit).not.toHaveBeenCalled();
270
+ });
271
+ it("encrypts PII fields on update", async () => {
272
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
273
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
274
+ await repo.updateForOwner("user", "user-1", "addr-1", { fullName: "New Name" });
275
+ expect(mockEncryptPiiFields).toHaveBeenCalledWith(expect.objectContaining({ fullName: "New Name" }), expect.arrayContaining(["fullName", "phone", "addressLine1"]));
276
+ });
277
+ });
278
+ // ---------------------------------------------------------------------------
279
+ // deleteForOwner
280
+ // ---------------------------------------------------------------------------
281
+ describe("AddressesRepository.deleteForOwner", () => {
282
+ it("throws DatabaseError when address not found", async () => {
283
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
284
+ await expect(repo.deleteForOwner("user", "user-1", "addr-x")).rejects.toThrow(DatabaseError);
285
+ });
286
+ it("throws DatabaseError when address belongs to a different ownerId", async () => {
287
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
288
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
289
+ await expect(repo.deleteForOwner("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
290
+ });
291
+ it("throws DatabaseError when address belongs to a different ownerType", async () => {
292
+ const address = makeAddressDoc({ ownerType: "store", ownerId: "user-1" });
293
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
294
+ await expect(repo.deleteForOwner("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
295
+ });
296
+ it("deletes the address when ownership matches", async () => {
297
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
298
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
299
+ await repo.deleteForOwner("user", "user-1", "addr-1");
300
+ expect(mockDocRef.delete).toHaveBeenCalledOnce();
301
+ });
302
+ it("does NOT delete address for a different store-owner", async () => {
303
+ const storeAddr = makeAddressDoc({ ownerType: "store", ownerId: "store-2" });
304
+ mockDocRef.get.mockResolvedValue(makeSnap(storeAddr, "addr-1"));
305
+ await expect(repo.deleteForOwner("store", "store-1", "addr-1")).rejects.toThrow(DatabaseError);
306
+ expect(mockDocRef.delete).not.toHaveBeenCalled();
307
+ });
308
+ });
309
+ // ---------------------------------------------------------------------------
310
+ // setDefault
311
+ // ---------------------------------------------------------------------------
312
+ describe("AddressesRepository.setDefault", () => {
313
+ it("throws DatabaseError when address not found", async () => {
314
+ mockDocRef.get.mockResolvedValue(makeSnap(null));
315
+ await expect(repo.setDefault("user", "user-1", "addr-x")).rejects.toThrow(DatabaseError);
316
+ });
317
+ it("throws DatabaseError when address belongs to a different owner", async () => {
318
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-other" });
319
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
320
+ await expect(repo.setDefault("user", "user-1", "addr-1")).rejects.toThrow(DatabaseError);
321
+ });
322
+ it("clears all existing defaults for the owner before setting the new one", async () => {
323
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
324
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
325
+ const existingDefault = makeAddressDoc({ isDefault: true, id: "addr-old" });
326
+ const existingSnap = makeSnap(existingDefault, "addr-old");
327
+ mockQuery.get.mockResolvedValue({ docs: [existingSnap], empty: false, size: 1 });
328
+ await repo.setDefault("user", "user-1", "addr-1");
329
+ // clearDefaultFlag ran and batched the update
330
+ expect(mockBatch.update).toHaveBeenCalledWith(existingSnap.ref, expect.objectContaining({ isDefault: false }));
331
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
332
+ });
333
+ it("sets isDefault:true on the target address", async () => {
334
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
335
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
336
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
337
+ await repo.setDefault("user", "user-1", "addr-1");
338
+ expect(mockDocRef.update).toHaveBeenCalledWith(expect.objectContaining({ isDefault: true }));
339
+ });
340
+ it("uses a batch for atomicity (clear + set in single WriteBatch)", async () => {
341
+ const address = makeAddressDoc({ ownerType: "user", ownerId: "user-1" });
342
+ mockDocRef.get.mockResolvedValue(makeSnap(address, "addr-1"));
343
+ // Two existing defaults to clear
344
+ const s1 = makeSnap(makeAddressDoc({ isDefault: true, id: "old-1" }), "old-1");
345
+ const s2 = makeSnap(makeAddressDoc({ isDefault: true, id: "old-2" }), "old-2");
346
+ mockQuery.get.mockResolvedValue({ docs: [s1, s2], empty: false, size: 2 });
347
+ await repo.setDefault("user", "user-1", "addr-1");
348
+ expect(mockBatch.update).toHaveBeenCalledTimes(2);
349
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
350
+ });
351
+ });
352
+ // ---------------------------------------------------------------------------
353
+ // deleteAllForOwner
354
+ // ---------------------------------------------------------------------------
355
+ describe("AddressesRepository.deleteAllForOwner", () => {
356
+ it("returns 0 and does not start a batch when no addresses found", async () => {
357
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
358
+ const count = await repo.deleteAllForOwner("user", "user-1");
359
+ expect(count).toBe(0);
360
+ expect(mockBatch.delete).not.toHaveBeenCalled();
361
+ expect(mockBatch.commit).not.toHaveBeenCalled();
362
+ });
363
+ it("batch-deletes all matching addresses and returns the count", async () => {
364
+ const s1 = makeSnap(makeAddressDoc({ id: "addr-1" }), "addr-1");
365
+ const s2 = makeSnap(makeAddressDoc({ id: "addr-2" }), "addr-2");
366
+ mockQuery.get.mockResolvedValue({ docs: [s1, s2], empty: false, size: 2 });
367
+ const count = await repo.deleteAllForOwner("user", "user-1");
368
+ expect(count).toBe(2);
369
+ expect(mockBatch.delete).toHaveBeenCalledWith(s1.ref);
370
+ expect(mockBatch.delete).toHaveBeenCalledWith(s2.ref);
371
+ expect(mockBatch.commit).toHaveBeenCalledOnce();
372
+ });
373
+ it("queries by ownerType AND ownerId", async () => {
374
+ mockQuery.get.mockResolvedValue({ docs: [], empty: true, size: 0 });
375
+ await repo.deleteAllForOwner("store", "store-xyz");
376
+ expect(mockCollection.where).toHaveBeenCalledWith("ownerType", "==", "store");
377
+ expect(mockQuery.where).toHaveBeenCalledWith("ownerId", "==", "store-xyz");
378
+ });
379
+ it("returns count matching the number of docs deleted", async () => {
380
+ const snaps = Array.from({ length: 5 }, (_, i) => makeSnap(makeAddressDoc({ id: `addr-${i}` }), `addr-${i}`));
381
+ mockQuery.get.mockResolvedValue({ docs: snaps, empty: false, size: 5 });
382
+ const count = await repo.deleteAllForOwner("user", "user-1");
383
+ expect(count).toBe(5);
384
+ expect(mockBatch.delete).toHaveBeenCalledTimes(5);
385
+ });
386
+ });
@@ -7,9 +7,9 @@
7
7
  * are encrypted at rest via the existing `encryptPiiFields` pipeline; the
8
8
  * repository overrides `createWithId` and `create` to inject encryption.
9
9
  */
10
+ import type { BaseDocument } from "../../../_internal/shared/types/base-document";
10
11
  export type AddressOwnerType = "user" | "store";
11
- export interface AddressDocument {
12
- id: string;
12
+ export interface AddressDocument extends BaseDocument {
13
13
  ownerType: AddressOwnerType;
14
14
  ownerId: string;
15
15
  label: string;
@@ -23,8 +23,6 @@ export interface AddressDocument {
23
23
  postalCode: string;
24
24
  country: string;
25
25
  isDefault: boolean;
26
- createdAt: Date;
27
- updatedAt: Date;
28
26
  }
29
27
  export declare const ADDRESSES_COLLECTION: "addresses";
30
28
  export declare const ADDRESS_INDEXED_FIELDS: readonly ["ownerType", "ownerId", "isDefault", "createdAt"];
@@ -9,6 +9,6 @@
9
9
  * export { adminBidsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminCouponsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminProductsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -9,6 +9,6 @@
9
9
  * export { adminReviewsGET as GET } from "@mohasinac/feat-admin";
10
10
  * ```
11
11
  */
12
- export declare const GET: (request: Request, context: {
12
+ export declare const GET: (request: Request, context?: {
13
13
  params: Promise<Record<string, string>>;
14
- }) => Promise<Response>;
14
+ } | undefined) => Promise<Response>;
@@ -0,0 +1,6 @@
1
+ export interface AdminFulfillmentViewProps {
2
+ /** Pre-selected store ID — skips the store picker when provided. */
3
+ initialStoreId?: string;
4
+ initialStoreName?: string;
5
+ }
6
+ export declare function AdminFulfillmentView({ initialStoreId, initialStoreName, }: AdminFulfillmentViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { Heading, Text } from "../../../ui/components/Typography";
5
+ import { Stack, Row } from "../../../ui/components/Layout";
6
+ import { Button } from "../../../ui/components/Button";
7
+ import { Badge } from "../../../ui/components/Badge";
8
+ import { PaginatedSelect } from "../../../ui/components/PaginatedSelect";
9
+ import { apiClient } from "../../../http";
10
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { normalizeError } from "../../../errors/normalize";
12
+ import { useToast } from "../../../ui";
13
+ async function loadStoreOptions(query, page) {
14
+ const params = new URLSearchParams({ page: String(page), pageSize: "25" });
15
+ if (query)
16
+ params.set("q", query);
17
+ const res = await apiClient.get(`${ADMIN_ENDPOINTS.STORES}?${params.toString()}`);
18
+ const items = (res.items ?? []).map((s) => ({ value: s.id, label: s.storeName }));
19
+ return { items, hasMore: res.hasMore ?? false };
20
+ }
21
+ function statusBadgeVariant(status) {
22
+ if (status === "confirmed")
23
+ return "warning";
24
+ if (status === "processing")
25
+ return "info";
26
+ if (status === "shipped")
27
+ return "success";
28
+ return "default";
29
+ }
30
+ export function AdminFulfillmentView({ initialStoreId, initialStoreName, }) {
31
+ const [storeId, setStoreId] = useState(initialStoreId ?? null);
32
+ const [orders, setOrders] = useState([]);
33
+ const [isLoading, setIsLoading] = useState(false);
34
+ const [actionLoadingId, setActionLoadingId] = useState(null);
35
+ const { showToast } = useToast();
36
+ const fetchOrders = useCallback(async (sid) => {
37
+ // toast-intentionally-silent: background data loader — stale list is kept on error
38
+ setIsLoading(true);
39
+ try {
40
+ const res = await apiClient.get(ADMIN_ENDPOINTS.ADMIN_FULFILLMENT(sid));
41
+ setOrders(res.orders ?? []);
42
+ }
43
+ catch {
44
+ // keep stale list on error
45
+ }
46
+ finally {
47
+ setIsLoading(false);
48
+ }
49
+ }, []);
50
+ useEffect(() => {
51
+ if (storeId)
52
+ void fetchOrders(storeId);
53
+ }, [storeId, fetchOrders]);
54
+ const handleMarkPicked = useCallback(async (orderId) => {
55
+ if (!storeId)
56
+ return;
57
+ setActionLoadingId(orderId + ":picked");
58
+ try {
59
+ await apiClient.patch(`/api/store/orders/${orderId}`, { markPicked: true });
60
+ showToast("Order marked as picked.", "success");
61
+ await fetchOrders(storeId);
62
+ }
63
+ catch (err) {
64
+ showToast(normalizeError(err).message, "error");
65
+ }
66
+ finally {
67
+ setActionLoadingId(null);
68
+ }
69
+ }, [storeId, fetchOrders, showToast]);
70
+ const handleMarkPacked = useCallback(async (orderId) => {
71
+ if (!storeId)
72
+ return;
73
+ setActionLoadingId(orderId + ":packed");
74
+ try {
75
+ await apiClient.patch(`/api/store/orders/${orderId}`, { markPacked: true });
76
+ showToast("Order marked as packed.", "success");
77
+ await fetchOrders(storeId);
78
+ }
79
+ catch (err) {
80
+ showToast(normalizeError(err).message, "error");
81
+ }
82
+ finally {
83
+ setActionLoadingId(null);
84
+ }
85
+ }, [storeId, fetchOrders, showToast]);
86
+ const handleUnassign = useCallback(async (orderId) => {
87
+ if (!storeId)
88
+ return;
89
+ setActionLoadingId(orderId + ":unassign");
90
+ try {
91
+ await apiClient.patch(`/api/store/orders/${orderId}/assign`, { workerId: null });
92
+ showToast("Worker unassigned.", "success");
93
+ await fetchOrders(storeId);
94
+ }
95
+ catch (err) {
96
+ showToast(normalizeError(err).message, "error");
97
+ }
98
+ finally {
99
+ setActionLoadingId(null);
100
+ }
101
+ }, [storeId, fetchOrders, showToast]);
102
+ return (_jsxs(Stack, { gap: "md", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsx(Heading, { level: 3, children: "Fulfillment Queue" }), storeId && (_jsx(Button, { variant: "secondary", size: "sm", onClick: () => {
103
+ setStoreId(null);
104
+ setOrders([]);
105
+ }, children: "Change Store" }))] }), !storeId && (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", color: "muted", children: "Select a store to view its fulfilment queue." }), _jsx(PaginatedSelect, { value: null, onChange: (v) => { if (v)
106
+ setStoreId(v); }, loadOptions: loadStoreOptions, placeholder: "Search stores\u2026", searchPlaceholder: "Type store name\u2026", noResultsText: "No stores found", ariaLabel: "Store" })] })), storeId && (_jsxs(Stack, { gap: "sm", children: [isLoading && (_jsx(Text, { size: "sm", color: "muted", children: "Loading orders\u2026" })), !isLoading && orders.length === 0 && (_jsx(Text, { color: "muted", children: "No orders in the fulfillment queue \u2014 great work!" })), orders.map((order) => (_jsxs(Stack, { surface: "card", padding: "sm", rounded: "md", gap: "xs", children: [_jsxs(Row, { align: "center", gap: "xs", wrap: true, children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["#", order.id] }), _jsx(Badge, { variant: statusBadgeVariant(order.status), children: order.status }), order.pickedAt && _jsx(Badge, { variant: "success", children: "Picked" }), order.packedAt && _jsx(Badge, { variant: "success", children: "Packed" })] }), _jsxs(Text, { size: "xs", color: "muted", children: [order.userName, order.items && order.items.length > 0 && (_jsxs(_Fragment, { children: [" \u00B7 ", order.items.map((i) => i.productTitle).join(", ")] }))] }), order.assignedWorkerId && (_jsxs(Text, { size: "xs", color: "muted", children: ["Assigned to: ", order.assignedWorkerId] })), _jsxs(Row, { gap: "xs", children: [!order.pickedAt && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => handleMarkPicked(order.id), isLoading: actionLoadingId === order.id + ":picked", children: "Mark Picked" })), order.pickedAt && !order.packedAt && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => handleMarkPacked(order.id), isLoading: actionLoadingId === order.id + ":packed", children: "Mark Packed" })), order.assignedWorkerId && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => handleUnassign(order.id), isLoading: actionLoadingId === order.id + ":unassign", children: "Unassign" }))] })] }, order.id)))] })), initialStoreName && !storeId && (_jsxs(Text, { size: "xs", color: "muted", children: ["Previously viewing: ", initialStoreName] }))] }));
107
+ }
@@ -13,8 +13,10 @@ import { GroupSettingsPanel } from "../../products/components/GroupSettingsPanel
13
13
  import { GroupInlineSelect } from "../../seller/components/GroupInlineSelect";
14
14
  import { CategoryQuickCreateForm } from "./CategoryQuickCreateForm";
15
15
  import { BrandQuickCreateForm } from "./BrandQuickCreateForm";
16
+ import { BarcodeField } from "../../seller/components/BarcodeField";
16
17
  const __P = {
17
18
  p4: "p-4",
19
+ sectionLabel: "tracking-widest mb-4",
18
20
  };
19
21
  const __O = {
20
22
  yAuto: "overflow-y-auto",
@@ -25,6 +27,14 @@ function modeFromProduct(product) {
25
27
  return "auction";
26
28
  if (lt === "pre-order")
27
29
  return "preorder";
30
+ if (lt === "prize-draw")
31
+ return "prize-draw";
32
+ if (lt === "classified")
33
+ return "classified";
34
+ if (lt === "digital-code")
35
+ return "digital-code";
36
+ if (lt === "live")
37
+ return "live";
28
38
  return "standard";
29
39
  }
30
40
  /**
@@ -32,14 +42,16 @@ function modeFromProduct(product) {
32
42
  * `listingType` discriminator. Nothing else needs to be written for the mode flip.
33
43
  */
34
44
  function applyMode(product, mode) {
35
- return {
36
- ...product,
37
- listingType: mode === "auction"
38
- ? "auction"
39
- : mode === "preorder"
40
- ? "pre-order"
41
- : "standard",
45
+ const map = {
46
+ standard: "standard",
47
+ auction: "auction",
48
+ preorder: "pre-order",
49
+ "prize-draw": "prize-draw",
50
+ classified: "classified",
51
+ "digital-code": "digital-code",
52
+ live: "live",
42
53
  };
54
+ return { ...product, listingType: map[mode] };
43
55
  }
44
56
  async function loadStoreOptions(query, page) {
45
57
  const params = new URLSearchParams({ page: String(page), pageSize: "25" });
@@ -154,9 +166,9 @@ export function AdminProductEditorView({ productId, onSaved, onDeleted, embedded
154
166
  const formContent = (_jsxs(Form, { id: "product-editor-form", onSubmit: (e) => {
155
167
  e.preventDefault();
156
168
  saveMutation.mutate();
157
- }, spacing: "lg", children: [_jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "tracking-widest mb-4", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Listing Type" }), _jsxs(Tabs, { value: mode, onChange: handleModeChange, children: [_jsxs(TabsList, { children: [(!enabledListingTypes || enabledListingTypes.includes("standard")) && (_jsx(TabsTrigger, { value: "standard", children: "Standard" })), (!enabledListingTypes || enabledListingTypes.includes("auction")) && (_jsx(TabsTrigger, { value: "auction", children: "Auction" })), (!enabledListingTypes || enabledListingTypes.includes("pre-order")) && (_jsx(TabsTrigger, { value: "preorder", children: "Pre-order" }))] }), _jsx(TabsContent, { value: "standard" }), _jsx(TabsContent, { value: "auction" }), _jsx(TabsContent, { value: "preorder" })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "tracking-widest mb-4", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Classification" }), _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: "Store" }), _jsx(PaginatedSelect, { value: product.storeId ?? null, onChange: handleStoreSelect, loadOptions: loadStoreOptions, placeholder: "Search stores\u2026", searchPlaceholder: "Type store name\u2026", noResultsText: "No stores found", ariaLabel: "Store" })] }), _jsx(ProductForm, { product: product, onChange: setProduct, renderGroupSettings: isEdit && productId
169
+ }, spacing: "lg", children: [_jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: __P.sectionLabel, color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Listing Type" }), _jsxs(Tabs, { value: mode, onChange: handleModeChange, children: [_jsxs(TabsList, { children: [(!enabledListingTypes || enabledListingTypes.includes("standard")) && (_jsx(TabsTrigger, { value: "standard", children: "Standard" })), (!enabledListingTypes || enabledListingTypes.includes("auction")) && (_jsx(TabsTrigger, { value: "auction", children: "Auction" })), (!enabledListingTypes || enabledListingTypes.includes("pre-order")) && (_jsx(TabsTrigger, { value: "preorder", children: "Pre-order" })), (!enabledListingTypes || enabledListingTypes.includes("prize-draw")) && (_jsx(TabsTrigger, { value: "prize-draw", children: "Prize Draw" })), (!enabledListingTypes || enabledListingTypes.includes("classified")) && (_jsx(TabsTrigger, { value: "classified", children: "Classified" })), (!enabledListingTypes || enabledListingTypes.includes("digital-code")) && (_jsx(TabsTrigger, { value: "digital-code", children: "Digital Code" })), (!enabledListingTypes || enabledListingTypes.includes("live")) && (_jsx(TabsTrigger, { value: "live", children: "Live Item" }))] }), _jsx(TabsContent, { value: "standard" }), _jsx(TabsContent, { value: "auction" }), _jsx(TabsContent, { value: "preorder" }), _jsx(TabsContent, { value: "prize-draw" }), _jsx(TabsContent, { value: "classified" }), _jsx(TabsContent, { value: "digital-code" }), _jsx(TabsContent, { value: "live" })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: __P.sectionLabel, color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Classification" }), _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: "Store" }), _jsx(PaginatedSelect, { value: product.storeId ?? null, onChange: handleStoreSelect, loadOptions: loadStoreOptions, placeholder: "Search stores\u2026", searchPlaceholder: "Type store name\u2026", noResultsText: "No stores found", ariaLabel: "Store" })] }), _jsx(ProductForm, { product: product, onChange: setProduct, renderGroupSettings: isEdit && productId
158
170
  ? (p) => (_jsx(GroupSettingsPanel, { productId: productId, productSlug: p.slug ?? productId, groupId: p.groupId, isGroupParent: p.isGroupParent, groupParentSlug: p.groupParentSlug, groupChildSlugs: p.groupChildSlugs, groupTitle: p.groupTitle, isAuction: modeFromProduct(p) === "auction", storeProductsEndpoint: "/api/admin/products", onGroupChanged: () => productQuery.refetch() }))
159
- : undefined, renderGroupJoinField: ({ label, value, onChange, disabled }) => (_jsx(GroupInlineSelect, { scope: "admin", value: value ?? "", onChange: (id) => onChange(id || undefined), disabled: disabled, label: label, placeholder: "None (standalone product)", allowCreate: false })), renderCategorySelector: ({ label, value, onChange, disabled }) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: label }), _jsx(PaginatedSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: label, disabled: disabled, createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })), renderBrandSelector: (args) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: args.label }), _jsx(PaginatedSelect, { value: args.multi ? null : (args.value || null), onChange: (v) => args.onValueChange(v ?? ""), loadOptions: loadBrandOptions, placeholder: "Search brands\u2026", searchPlaceholder: "Type brand name\u2026", noResultsText: "No brands found", ariaLabel: args.label, disabled: args.disabled, createLabel: "Brand", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(BrandQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })) })] })] }), _jsxs(Row, { gap: "3", className: "lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !product.title || isSubmitting, children: isEdit ? "Save changes" : "Create product" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete product" }))] })] }, "product-form"));
171
+ : undefined, renderGroupJoinField: ({ label, value, onChange, disabled }) => (_jsx(GroupInlineSelect, { scope: "admin", value: value ?? "", onChange: (id) => onChange(id || undefined), disabled: disabled, label: label, placeholder: "None (standalone product)", allowCreate: false })), renderCategorySelector: ({ label, value, onChange, disabled }) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: label }), _jsx(PaginatedSelect, { value: value || null, onChange: (v) => onChange(v ?? ""), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: label, disabled: disabled, createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })), renderBrandSelector: (args) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "primary", children: args.label }), _jsx(PaginatedSelect, { value: args.multi ? null : (args.value || null), onChange: (v) => args.onValueChange(v ?? ""), loadOptions: loadBrandOptions, placeholder: "Search brands\u2026", searchPlaceholder: "Type brand name\u2026", noResultsText: "No brands found", ariaLabel: args.label, disabled: args.disabled, createLabel: "Brand", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(BrandQuickCreateForm, { onSaved: (id, name) => onCreated({ value: id, label: name }), onCancel: onCancel })) })] })) })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: __P.sectionLabel, color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Inventory" }), _jsx(BarcodeField, { value: product.barcodeId ?? "", onChange: (v) => setProduct((prev) => ({ ...prev, barcodeId: v || undefined })), onScan: (v) => setProduct((prev) => ({ ...prev, barcodeId: v })), helperText: "Leave blank to auto-generate. Scan a pre-printed sticker to link it." })] }), _jsxs(Row, { gap: "3", className: "lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !product.title || isSubmitting, children: isEdit ? "Save changes" : "Create product" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete product" }))] })] }, "product-form"));
160
172
  if (embedded) {
161
173
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
162
174
  }