@mohasinac/appkit 4.9.5 → 4.11.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 (395) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
  3. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
  4. package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
  5. package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
  6. package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
  7. package/dist/_internal/server/features/auctions/actions.js +3 -2
  8. package/dist/_internal/server/features/auctions/service.d.ts +4 -3
  9. package/dist/_internal/server/features/auctions/service.js +6 -6
  10. package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
  11. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  12. package/dist/_internal/server/features/brands/actions.js +0 -2
  13. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  14. package/dist/_internal/server/features/brands/og.js +1 -1
  15. package/dist/_internal/server/features/bundles/data.d.ts +9 -0
  16. package/dist/_internal/server/features/bundles/data.js +14 -0
  17. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  18. package/dist/_internal/server/features/bundles/index.js +1 -1
  19. package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
  20. package/dist/_internal/server/features/catalogue/og.js +27 -0
  21. package/dist/_internal/server/features/grouped/data.d.ts +16 -0
  22. package/dist/_internal/server/features/grouped/data.js +50 -1
  23. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  24. package/dist/_internal/server/features/grouped/index.js +1 -1
  25. package/dist/_internal/server/features/orders/actions.js +9 -0
  26. package/dist/_internal/server/features/orders/adapters.js +25 -0
  27. package/dist/_internal/server/features/products/data.d.ts +23 -0
  28. package/dist/_internal/server/features/products/data.js +159 -0
  29. package/dist/_internal/server/features/products/index.d.ts +1 -1
  30. package/dist/_internal/server/features/products/index.js +1 -1
  31. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  32. package/dist/_internal/server/functions/scheduled.js +17 -1
  33. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  34. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  35. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  36. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  37. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  38. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  39. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  40. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  41. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  42. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  43. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  44. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  45. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  46. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  47. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  48. package/dist/_internal/server/notifications/channel-health.js +101 -0
  49. package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
  50. package/dist/_internal/shared/features/auctions/config.js +31 -1
  51. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  52. package/dist/_internal/shared/features/brands/schema.js +0 -1
  53. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  54. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  55. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  56. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
  57. package/dist/_internal/shared/listing-types/art/config.js +1 -1
  58. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
  59. package/dist/_internal/shared/listing-types/classified/config.js +1 -1
  60. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
  61. package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
  62. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
  63. package/dist/_internal/shared/listing-types/live/config.js +1 -1
  64. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
  65. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  66. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
  67. package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
  68. package/dist/client.d.ts +21 -12
  69. package/dist/client.js +14 -7
  70. package/dist/constants/api-endpoints.d.ts +9 -9
  71. package/dist/constants/api-endpoints.js +3 -3
  72. package/dist/contracts/client-auth.d.ts +3 -7
  73. package/dist/contracts/email.d.ts +1 -0
  74. package/dist/features/account/components/AddressForm.js +24 -2
  75. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  76. package/dist/features/account/components/BecomeSellerView.js +1 -1
  77. package/dist/features/account/components/NotificationBell.js +1 -1
  78. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  79. package/dist/features/account/components/UserBidsView.js +47 -0
  80. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  81. package/dist/features/account/components/UserOrdersView.js +54 -4
  82. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  83. package/dist/features/account/components/UserReturnsView.js +37 -3
  84. package/dist/features/account/components/index.d.ts +3 -2
  85. package/dist/features/account/components/index.js +1 -0
  86. package/dist/features/admin/actions/admin-actions.js +22 -0
  87. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  88. package/dist/features/admin/actions/notification-actions.js +4 -3
  89. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  90. package/dist/features/admin/components/AdminAdsView.js +86 -89
  91. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  92. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  93. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  94. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  95. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  96. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  97. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  98. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  99. package/dist/features/admin/components/AdminCartsView.js +34 -4
  100. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  101. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  102. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  103. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  104. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  105. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  106. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  107. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  108. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  109. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  110. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  111. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  112. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -4
  113. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  114. package/dist/features/admin/components/AdminStoresView.js +7 -0
  115. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  116. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  117. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  118. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  119. package/dist/features/admin/components/AdminUsersView.js +1 -1
  120. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  121. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  122. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  123. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  124. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  125. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  126. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  127. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  128. package/dist/features/admin/components/index.d.ts +8 -1
  129. package/dist/features/admin/components/index.js +4 -0
  130. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  131. package/dist/features/admin/constants/filter-tabs.js +27 -0
  132. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  133. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  134. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  135. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  136. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  137. package/dist/features/admin/schemas/firestore.d.ts +21 -0
  138. package/dist/features/admin/schemas/firestore.js +6 -0
  139. package/dist/features/analytics/types.d.ts +1 -1
  140. package/dist/features/analytics/types.js +8 -0
  141. package/dist/features/auctions/actions/bid-actions.js +4 -1
  142. package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
  143. package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
  144. package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
  145. package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
  146. package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
  147. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
  148. package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
  149. package/dist/features/auctions/schemas/index.d.ts +5 -0
  150. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  151. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  152. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  153. package/dist/features/audit-log/schemas/firestore.js +35 -0
  154. package/dist/features/auth/actions/index.d.ts +0 -1
  155. package/dist/features/auth/actions/index.js +0 -1
  156. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  157. package/dist/features/auth/hooks/useAuth.js +21 -37
  158. package/dist/features/auth/permissions/constants.d.ts +1 -1
  159. package/dist/features/auth/permissions/constants.js +2 -0
  160. package/dist/features/blog/schemas/index.d.ts +32 -0
  161. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  162. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  163. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
  164. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
  165. package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
  166. package/dist/features/catalogue/index.d.ts +2 -0
  167. package/dist/features/catalogue/index.js +1 -0
  168. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  169. package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
  170. package/dist/features/categories/components/BundleDetailView.js +3 -2
  171. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  172. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  173. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  174. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  175. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  176. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  177. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  178. package/dist/features/categories/schemas/index.d.ts +24 -3
  179. package/dist/features/categories/schemas/index.js +3 -1
  180. package/dist/features/categories/types/index.d.ts +7 -0
  181. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  182. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  183. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
  184. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
  185. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
  186. package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
  187. package/dist/features/contact/components/ContactForm.js +17 -4
  188. package/dist/features/contact/components/ContactPageView.js +8 -2
  189. package/dist/features/contact/email.d.ts +1 -0
  190. package/dist/features/contact/email.js +3 -0
  191. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
  192. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
  193. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  194. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  195. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  196. package/dist/features/events/components/EventFormDrawer.js +5 -1
  197. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  198. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  199. package/dist/features/events/schemas/index.d.ts +22 -0
  200. package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
  201. package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
  202. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +12 -0
  203. package/dist/features/grouped/repository/grouped-listings.repository.js +34 -0
  204. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  205. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  206. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  207. package/dist/features/layout/BottomActions.js +4 -3
  208. package/dist/features/layout/BottomActionsContext.d.ts +10 -0
  209. package/dist/features/layout/BottomActionsContext.js +5 -1
  210. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  211. package/dist/features/layout/TitleBarLayout.js +3 -3
  212. package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
  213. package/dist/features/layout/hooks/useBottomActions.js +4 -1
  214. package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
  215. package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
  216. package/dist/features/media/MediaImage.js +39 -3
  217. package/dist/features/media/MediaVideo.js +17 -1
  218. package/dist/features/media/types/index.d.ts +10 -0
  219. package/dist/features/media/types/index.js +1 -0
  220. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  221. package/dist/features/media/upload/MediaUploadField.js +11 -7
  222. package/dist/features/media/upload/video-poster.d.ts +2 -0
  223. package/dist/features/media/upload/video-poster.js +16 -1
  224. package/dist/features/media/upload/video-poster.test.js +1 -1
  225. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  226. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  227. package/dist/features/orders/components/index.d.ts +2 -0
  228. package/dist/features/orders/components/index.js +1 -0
  229. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  230. package/dist/features/orders/constants/payment-window.js +25 -0
  231. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  232. package/dist/features/orders/repository/orders.repository.js +56 -0
  233. package/dist/features/orders/schemas/index.d.ts +24 -24
  234. package/dist/features/orders/types/index.d.ts +33 -0
  235. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
  236. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  237. package/dist/features/products/components/BidHistory.js +9 -1
  238. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  239. package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
  240. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  241. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  242. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  243. package/dist/features/products/components/ProductDetailActions.js +1 -1
  244. package/dist/features/products/components/ProductDetailPageView.js +11 -116
  245. package/dist/features/products/components/ProductGrid.js +15 -1
  246. package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
  247. package/dist/features/products/components/RelatedItemsSection.js +17 -0
  248. package/dist/features/products/constants/action-defs.d.ts +1 -0
  249. package/dist/features/products/constants/action-defs.js +1 -0
  250. package/dist/features/products/schemas/index.d.ts +5 -0
  251. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  252. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  253. package/dist/features/search/components/SearchView.d.ts +1 -3
  254. package/dist/features/search/components/SearchView.js +1 -1
  255. package/dist/features/seller/components/QuickProductForm.js +2 -1
  256. package/dist/features/seller/components/SellerBidsView.js +66 -107
  257. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  258. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  259. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  260. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  261. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  262. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  263. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  264. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  265. package/dist/features/seller/components/SellerProductShell.js +90 -37
  266. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  267. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  268. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  269. package/dist/features/seller/components/SellerProductsView.js +131 -189
  270. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  271. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  272. package/dist/features/seller/components/SellerShippingView.js +41 -4
  273. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  274. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  275. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  276. package/dist/features/seller/components/index.d.ts +2 -2
  277. package/dist/features/seller/components/index.js +2 -2
  278. package/dist/features/shell/FormShell.d.ts +24 -4
  279. package/dist/features/shell/FormShell.js +12 -6
  280. package/dist/features/shell/QuickFormDrawer.js +67 -12
  281. package/dist/features/shell/StepForm.d.ts +30 -1
  282. package/dist/features/shell/StepForm.js +47 -4
  283. package/dist/features/shell/index.d.ts +1 -1
  284. package/dist/features/shell/index.js +1 -1
  285. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  286. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  287. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  288. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  289. package/dist/features/tester/components/TesterHubView.js +3 -2
  290. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  291. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  292. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +487 -18
  293. package/dist/index.d.ts +17 -3
  294. package/dist/index.js +23 -5
  295. package/dist/next/routing/route-map.d.ts +13 -0
  296. package/dist/next/routing/route-map.js +12 -0
  297. package/dist/providers/email-resend/provider.js +1 -0
  298. package/dist/providers/firebase-client/auth.d.ts +1 -0
  299. package/dist/providers/firebase-client/auth.js +7 -1
  300. package/dist/repositories/index.d.ts +1 -0
  301. package/dist/repositories/index.js +1 -0
  302. package/dist/schemas/registry.d.ts +61 -25
  303. package/dist/security/pii-mask.js +1 -1
  304. package/dist/seed/bids-seed-data.js +48 -3
  305. package/dist/seed/categories-seed-data.js +69 -0
  306. package/dist/seed/grouped-listings-seed-data.js +167 -0
  307. package/dist/seed/payouts-seed-data.js +4 -4
  308. package/dist/seed/products-art-seed-data.js +188 -5
  309. package/dist/seed/products-auctions-seed-data.js +226 -6
  310. package/dist/seed/products-classifieds-seed-data.js +255 -8
  311. package/dist/seed/products-digital-codes-seed-data.js +258 -19
  312. package/dist/seed/products-live-items-seed-data.js +143 -10
  313. package/dist/seed/products-preorders-seed-data.js +213 -8
  314. package/dist/seed/products-prize-draws-seed-data.js +198 -0
  315. package/dist/seed/products-standard-seed-data.js +234 -4
  316. package/dist/seed/products-stickers-seed-data.js +178 -5
  317. package/dist/seed/site-settings-seed-data.js +23 -30
  318. package/dist/seed/store-extensions-seed-data.js +2 -2
  319. package/dist/server-entry.d.ts +6 -2
  320. package/dist/server-entry.js +24 -3
  321. package/dist/server.d.ts +10 -3
  322. package/dist/server.js +11 -4
  323. package/dist/styles.css +29 -1
  324. package/dist/tailwind-utilities.css +1 -1
  325. package/dist/ui/components/Form.d.ts +11 -2
  326. package/dist/ui/components/Form.js +4 -3
  327. package/dist/ui/components/FormField.d.ts +3 -1
  328. package/dist/ui/components/FormField.js +22 -4
  329. package/dist/ui/components/ImageLightbox.js +37 -3
  330. package/dist/ui/components/ImageLightbox.style.css +28 -0
  331. package/dist/ui/components/ListingLayout.d.ts +1 -2
  332. package/dist/ui/components/ListingLayout.js +1 -1
  333. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  334. package/dist/ui/components/RecordDetailModal.js +8 -0
  335. package/dist/ui/components/SlottedListingView.js +9 -1
  336. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  337. package/dist/ui/forms/FormErrorSummary.js +37 -0
  338. package/dist/ui/forms/FormShell.d.ts +37 -2
  339. package/dist/ui/forms/FormShell.js +37 -7
  340. package/dist/ui/forms/FormShell.style.css +17 -0
  341. package/dist/ui/forms/index.d.ts +2 -0
  342. package/dist/ui/forms/index.js +1 -0
  343. package/dist/ui/index.d.ts +4 -0
  344. package/dist/ui/index.js +5 -0
  345. package/dist/utils/media-url.d.ts +12 -1
  346. package/dist/utils/media-url.js +59 -1
  347. package/dist/validation/schemas.d.ts +4 -4
  348. package/package.json +1 -1
  349. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  350. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  351. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  352. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  353. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  354. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  355. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  356. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  357. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  358. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  359. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  360. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  361. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  362. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  363. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  364. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  365. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  366. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  367. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  368. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  369. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  370. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  371. package/dist/features/auth/consent-otp.d.ts +0 -57
  372. package/dist/features/auth/consent-otp.js +0 -127
  373. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  374. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  375. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  376. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  377. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  378. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  379. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  380. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  381. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  382. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  383. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  384. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  385. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  386. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  387. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  388. package/dist/features/live/components/LiveItemsListView.js +0 -44
  389. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  390. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  391. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  392. package/dist/react/hooks/useVisibleItems.js +0 -59
  393. package/dist/ui/components/TabStrip.d.ts +0 -34
  394. package/dist/ui/components/TabStrip.js +0 -56
  395. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -7,6 +7,7 @@ import { serverLogger } from "../../../../monitoring/server-logger";
7
7
  import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
8
8
  import { PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
9
9
  import { PRODUCT_FIELDS } from "../../../../constants/field-names";
10
+ import { SIEVE_OP, sieveAnd, sieveFilter, sortBy } from "../../../../utils/sieve-builder";
10
11
  // ---------------------------------------------------------------------------
11
12
  // Request-scoped cache — each function is deduplicated per RSC render tree.
12
13
  // Both generateMetadata() and the page component get the same promise.
@@ -23,6 +24,38 @@ export const getProductForDetail = cache(async (slugOrId) => {
23
24
  export const getReviewsForProduct = cache(async (productId) => {
24
25
  return reviewRepository.findApprovedByProduct(productId).catch(() => []);
25
26
  });
27
+ /** Firestore review doc → client Review shape. Shared by every listing-type detail page's reviews tab. */
28
+ export function toReview(doc) {
29
+ const images = Array.isArray(doc.images) ? doc.images : undefined;
30
+ const createdAt = doc.createdAt instanceof Date
31
+ ? doc.createdAt.toISOString()
32
+ : typeof doc.createdAt === "string"
33
+ ? doc.createdAt
34
+ : undefined;
35
+ const rawRating = typeof doc.rating === "number" ? doc.rating : 3;
36
+ const rating = Math.min(5, Math.max(1, Math.round(rawRating)));
37
+ return {
38
+ id: String(doc.id ?? ""),
39
+ productId: String(doc.productId ?? ""),
40
+ userId: String(doc.userId ?? ""),
41
+ userName: String(doc.userName ?? "Anonymous"),
42
+ userAvatar: typeof doc.userAvatar === "string" ? doc.userAvatar : undefined,
43
+ rating,
44
+ title: typeof doc.title === "string" ? doc.title : undefined,
45
+ comment: typeof doc.comment === "string" ? doc.comment : undefined,
46
+ images,
47
+ status: doc.status ?? "approved",
48
+ helpfulCount: typeof doc.helpfulCount === "number" ? doc.helpfulCount : undefined,
49
+ verified: doc.verified === true,
50
+ featured: doc.featured === true,
51
+ createdAt,
52
+ };
53
+ }
54
+ /** Fetch + map approved reviews for a product straight to the client Review shape, deduped per request. */
55
+ export const getReviewItemsForProduct = cache(async (productId) => {
56
+ const docs = await reviewRepository.findApprovedByProduct(productId).catch(() => []);
57
+ return docs.map(toReview);
58
+ });
26
59
  /** List all published products for sitemap generation. */
27
60
  export async function listSitemapProducts() {
28
61
  try {
@@ -54,3 +87,129 @@ export async function listSitemapProducts() {
54
87
  return [];
55
88
  }
56
89
  }
90
+ // ---------------------------------------------------------------------------
91
+ // Related items — same 4-signal computation shared by every listing-type
92
+ // detail page (category / brand / tag-overlap / same-store).
93
+ // ---------------------------------------------------------------------------
94
+ const RELATED_QUERY_PAGE_SIZE = 9;
95
+ const RELATED_DISPLAY_LIMIT = 8;
96
+ /** Firestore doc → card-grid shape. Shared by every related/similar-items carousel. */
97
+ export function toProductItem(doc) {
98
+ return {
99
+ id: String(doc.id ?? ""),
100
+ title: String(doc.title ?? doc.name ?? ""),
101
+ price: typeof doc.price === "number" ? doc.price : 0,
102
+ originalPrice: typeof doc.originalPrice === "number" ? doc.originalPrice : undefined,
103
+ mainImage: Array.isArray(doc.images)
104
+ ? doc.images[0]
105
+ : typeof doc.mainImage === "string"
106
+ ? doc.mainImage
107
+ : undefined,
108
+ status: doc.status ?? "published",
109
+ slug: typeof doc.slug === "string" ? doc.slug : undefined,
110
+ storeName: typeof doc.storeName === "string" ? doc.storeName : undefined,
111
+ rating: typeof doc.rating === "number" ? doc.rating : undefined,
112
+ reviewCount: typeof doc.reviewCount === "number" ? doc.reviewCount : undefined,
113
+ // Without this, every related-item card silently linked to the standard
114
+ // PDP regardless of its real type (pluginFor() defaults to "standard"
115
+ // when listingType is missing) — same bug class as the Phase 1 routing
116
+ // fix, one hop further downstream in the related-items card link path.
117
+ listingType: doc.listingType ?? undefined,
118
+ };
119
+ }
120
+ /**
121
+ * Excludes the current product plus any listing-type-specific "invalid to
122
+ * surface as related" state: sold/archived/out-of-stock/draft, ended
123
+ * auctions, closed prize-draws, sold-out pre-orders, depleted digital-code
124
+ * pools.
125
+ */
126
+ function isValidRelatedItem(r, currentProductId, now) {
127
+ if (r.id === currentProductId)
128
+ return false;
129
+ const s = r.status;
130
+ if (s && ["sold", "out_of_stock", "archived", "discontinued", "draft"].includes(s))
131
+ return false;
132
+ if (r.isSold === true)
133
+ return false;
134
+ if (r.availableQuantity === 0)
135
+ return false;
136
+ if (r.listingType === "auction" && r.auctionEndDate) {
137
+ const end = r.auctionEndDate;
138
+ const endDate = typeof end.toDate === "function"
139
+ ? end.toDate()
140
+ : end instanceof Date
141
+ ? end
142
+ : new Date(String(end));
143
+ if (endDate <= now)
144
+ return false;
145
+ }
146
+ if (r.listingType === "prize-draw" && r.prizeRevealStatus === "closed")
147
+ return false;
148
+ if (r.listingType === "pre-order" &&
149
+ typeof r.preOrderMaxQuantity === "number" &&
150
+ typeof r.preOrderCurrentCount === "number" &&
151
+ r.preOrderCurrentCount >= r.preOrderMaxQuantity) {
152
+ return false;
153
+ }
154
+ if (r.listingType === "digital-code" && r.codesAvailable === 0)
155
+ return false;
156
+ return true;
157
+ }
158
+ function toRelatedItems(docs, currentProductId) {
159
+ const now = new Date();
160
+ return docs
161
+ .filter((r) => isValidRelatedItem(r, currentProductId, now))
162
+ .slice(0, RELATED_DISPLAY_LIMIT)
163
+ .map(toProductItem);
164
+ }
165
+ /**
166
+ * Same 4-signal related-items computation used across every listing-type
167
+ * detail page (category / brand / tag-overlap / same-store), each excluding
168
+ * invalid states for that item's own listing type. Extracted from
169
+ * ProductDetailPageView so pre-order/prize-draw/classified/digital-code/live
170
+ * pages can reuse it instead of having no related section at all.
171
+ */
172
+ export const computeRelatedItems = cache(async (product) => {
173
+ const p = product;
174
+ const primaryCategorySlug = Array.isArray(p.categorySlugs) ? p.categorySlugs[0] : undefined;
175
+ const brand = typeof p.brand === "string" ? p.brand : undefined;
176
+ const tags = Array.isArray(p.tags) ? p.tags : [];
177
+ const storeId = typeof p.storeId === "string" ? p.storeId : undefined;
178
+ const currentProductId = product.id;
179
+ const [sameCategoryDocs, sameBrandDocs, tagOverlapDocs, sameStoreDocs] = await Promise.all([
180
+ primaryCategorySlug
181
+ ? productRepository.findByCategory(primaryCategorySlug).catch(() => [])
182
+ : Promise.resolve([]),
183
+ brand
184
+ ? productRepository
185
+ .list({
186
+ filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("brand", SIEVE_OP.EQ, brand)),
187
+ sorts: sortBy("createdAt", "DESC"),
188
+ page: 1,
189
+ pageSize: RELATED_QUERY_PAGE_SIZE,
190
+ })
191
+ .then((r) => r.items)
192
+ .catch(() => [])
193
+ : Promise.resolve([]),
194
+ tags.length > 0
195
+ ? productRepository.findByTagsOverlap(tags, RELATED_QUERY_PAGE_SIZE).catch(() => [])
196
+ : Promise.resolve([]),
197
+ storeId
198
+ ? productRepository
199
+ .list({
200
+ filters: sieveAnd(sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("storeId", SIEVE_OP.EQ, storeId)),
201
+ sorts: sortBy("createdAt", "DESC"),
202
+ page: 1,
203
+ pageSize: RELATED_QUERY_PAGE_SIZE,
204
+ })
205
+ .then((r) => r.items)
206
+ .catch(() => [])
207
+ : Promise.resolve([]),
208
+ ]);
209
+ return {
210
+ relatedItems: toRelatedItems(sameCategoryDocs, currentProductId),
211
+ relatedByBrand: toRelatedItems(sameBrandDocs, currentProductId),
212
+ relatedByTags: toRelatedItems(tagOverlapDocs, currentProductId),
213
+ relatedByStore: toRelatedItems(sameStoreDocs, currentProductId),
214
+ };
215
+ });
@@ -1,4 +1,4 @@
1
- export { getProductForDetail, getReviewsForProduct, listSitemapProducts, type SitemapProduct, } from "./data";
1
+ export { getProductForDetail, getReviewsForProduct, listSitemapProducts, type SitemapProduct, computeRelatedItems, toProductItem, toReview, getReviewItemsForProduct, type RelatedItemsResult, } from "./data";
2
2
  export { assertProductOwnership, assertStatusTransition, assertInStock, effectivePrice, isAvailableForPurchase, } from "./service";
3
3
  export { createProductAction, createAuctionAction, createPreOrderAction, updateProductAction, deleteProductAction, setProductStatusAction, setProductFeaturedAction, } from "./actions";
4
4
  export { PRODUCTS_PAGE_SIZE, PRODUCTS_FEATURED_LIMIT, PRODUCTS_RELATED_LIMIT, PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
@@ -1,4 +1,4 @@
1
- export { getProductForDetail, getReviewsForProduct, listSitemapProducts, } from "./data";
1
+ export { getProductForDetail, getReviewsForProduct, listSitemapProducts, computeRelatedItems, toProductItem, toReview, getReviewItemsForProduct, } from "./data";
2
2
  export { assertProductOwnership, assertStatusTransition, assertInStock, effectivePrice, isAvailableForPurchase, } from "./service";
3
3
  export { createProductAction, createAuctionAction, createPreOrderAction, updateProductAction, deleteProductAction, setProductStatusAction, setProductFeaturedAction, } from "./actions";
4
4
  export { PRODUCTS_PAGE_SIZE, PRODUCTS_FEATURED_LIMIT, PRODUCTS_RELATED_LIMIT, PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
@@ -7,6 +7,7 @@ export declare const couponExpiry: import("./types").ScheduledFunctionDefinition
7
7
  export declare const offerExpiry: import("./types").ScheduledFunctionDefinition;
8
8
  export declare const productStatsSync: import("./types").ScheduledFunctionDefinition;
9
9
  export declare const revenueRollup: import("./types").ScheduledFunctionDefinition;
10
+ export declare const dailyStatusDigest: import("./types").ScheduledFunctionDefinition;
10
11
  export declare const dailyDataCleanup: import("./types").ScheduledFunctionDefinition;
11
12
  export declare const countersReconcile: import("./types").ScheduledFunctionDefinition;
12
13
  export declare const positionsReconcile: import("./types").ScheduledFunctionDefinition;
@@ -19,8 +20,9 @@ export declare const cleanupRtdbEvents: import("./types").ScheduledFunctionDefin
19
20
  export declare const mediaTmpCleanup: import("./types").ScheduledFunctionDefinition;
20
21
  export declare const draftPrune: import("./types").ScheduledFunctionDefinition;
21
22
  export declare const testerSandboxCleanup: import("./types").ScheduledFunctionDefinition;
23
+ export declare const testerSandboxRefresh: import("./types").ScheduledFunctionDefinition;
22
24
  export declare const prizeDrawExpiryReveal: import("./types").ScheduledFunctionDefinition;
23
25
  export declare const bundleStockSync: import("./types").ScheduledFunctionDefinition;
24
26
  export declare const emiInstallmentReminder: import("./types").ScheduledFunctionDefinition;
25
27
  export declare const catalogueImageStalenessReminder: import("./types").ScheduledFunctionDefinition;
26
- export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
28
+ export declare const SCHEDULED_FUNCTIONS: readonly [import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition, import("./types").ScheduledFunctionDefinition];
@@ -1,6 +1,6 @@
1
1
  // Scheduled function definitions (Cloud Scheduler triggers).
2
2
  // Cron syntax accepts both Firebase shorthand and standard cron expressions.
3
- import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, payoutBatchHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, weeklyPayoutEligibilityHandler, testerSandboxCleanupHandler, } from "../jobs/handlers";
3
+ import { auctionSettlementHandler, autoPayoutEligibilityHandler, bundleStockSyncHandler, cartPruneHandler, cleanupRtdbEventsHandler, countersReconcileHandler, couponExpiryHandler, dailyDataCleanupHandler, draftPruneHandler, emiInstallmentReminderHandler, catalogueImageStalenessReminderHandler, mediaTmpCleanupHandler, notificationPruneHandler, offerExpiryHandler, payoutBatchHandler, paymentWindowTimeoutHandler, hardBanReinstatementHandler, paymentReviewAutoApproveHandler, pendingOrderTimeoutHandler, positionsReconcileHandler, prizeDrawExpiryRevealHandler, productStatsSyncHandler, revenueRollupHandler, dailyStatusDigestHandler, weeklyPayoutEligibilityHandler, testerSandboxCleanupHandler, testerSandboxRefreshHandler, } from "../jobs/handlers";
4
4
  import { defineFunction } from "./define";
5
5
  const REGION = "asia-south1";
6
6
  const EVERY_5_MIN = "every 5 minutes";
@@ -67,6 +67,13 @@ export const revenueRollup = defineFunction({
67
67
  handler: revenueRollupHandler,
68
68
  options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
69
69
  });
70
+ export const dailyStatusDigest = defineFunction({
71
+ name: "dailyStatusDigest",
72
+ description: "Email the previous 24h order summary + platform status to the team every morning at 10:00 IST.",
73
+ trigger: { kind: "schedule", cron: "0 10 * * *", timeZone: "Asia/Kolkata" },
74
+ handler: dailyStatusDigestHandler,
75
+ options: { region: REGION, timeoutSeconds: 120, memory: "256MiB", maxInstances: 1 },
76
+ });
70
77
  export const dailyDataCleanup = defineFunction({
71
78
  name: "dailyDataCleanup",
72
79
  description: "Daily data cleanup (drafts, transient records) at 02:00 UTC.",
@@ -151,6 +158,13 @@ export const testerSandboxCleanup = defineFunction({
151
158
  handler: testerSandboxCleanupHandler,
152
159
  options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
153
160
  });
161
+ export const testerSandboxRefresh = defineFunction({
162
+ name: "testerSandboxRefresh",
163
+ description: "Revert live tester QA sandbox fixtures to their canonical seed shape and prune tester-created extras (every 4 hours).",
164
+ trigger: { kind: "schedule", cron: "0 */4 * * *", timeZone: "UTC" },
165
+ handler: testerSandboxRefreshHandler,
166
+ options: { region: REGION, timeoutSeconds: 300, memory: "256MiB", maxInstances: 1 },
167
+ });
154
168
  export const prizeDrawExpiryReveal = defineFunction({
155
169
  name: "prizeDrawExpiryReveal",
156
170
  description: "Assign winners for outstanding paid orders on scheduled-mode prize draws past their expiry, then close them (every 5 minutes).",
@@ -189,6 +203,7 @@ export const SCHEDULED_FUNCTIONS = [
189
203
  offerExpiry,
190
204
  productStatsSync,
191
205
  revenueRollup,
206
+ dailyStatusDigest,
192
207
  dailyDataCleanup,
193
208
  countersReconcile,
194
209
  positionsReconcile,
@@ -201,6 +216,7 @@ export const SCHEDULED_FUNCTIONS = [
201
216
  mediaTmpCleanup,
202
217
  draftPrune,
203
218
  testerSandboxCleanup,
219
+ testerSandboxRefresh,
204
220
  prizeDrawExpiryReveal,
205
221
  bundleStockSync,
206
222
  emiInstallmentReminder,
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Core: daily status digest email.
3
+ *
4
+ * Emails the previous 24h of order activity to the team every morning. The
5
+ * digest's arrival is itself the platform health signal — if it stops
6
+ * landing, something in the Firestore → Functions → Resend chain needs a
7
+ * look. Deliberately no separate error/exception tracking: there is no
8
+ * structured error-log collection to query, and building one just to fill
9
+ * this email would be scope no one asked for.
10
+ *
11
+ * Runs from a scheduled Firebase Function (not a Vercel route), so the
12
+ * unbounded-ish order scan below is safe against CLAUDE.md Rule #6's 10s
13
+ * sync ceiling — it has the Function's much larger budget.
14
+ */
15
+ import type { JobContext } from "../runtime/types";
16
+ export interface DailyStatusDigestSummary {
17
+ orderCount: number;
18
+ revenue: number;
19
+ statusBreakdown: Record<string, number>;
20
+ stuckPendingCount: number;
21
+ activeListingCount: number;
22
+ }
23
+ /** Gathers the last-24h numbers. Exported so the admin manual-trigger route can reuse it. */
24
+ export declare function collectDailyStatusSummary(): Promise<DailyStatusDigestSummary>;
25
+ export interface DailyStatusDigestResult {
26
+ sent: boolean;
27
+ reason?: string;
28
+ summary?: DailyStatusDigestSummary;
29
+ }
30
+ /**
31
+ * Sends the digest once per deployment version.
32
+ *
33
+ * Called from server startup. On Vercel that means EVERY lambda cold start,
34
+ * not once per deploy — so the version marker below is what makes this a
35
+ * per-deployment event rather than a per-cold-start email storm. The claim
36
+ * runs in a transaction because several cold starts can race on the first
37
+ * request after a deploy; exactly one wins and sends.
38
+ */
39
+ export declare function runDeploymentDigest(ctx: JobContext, version: string): Promise<DailyStatusDigestResult>;
40
+ export declare function runDailyStatusDigest(ctx: JobContext, version?: string): Promise<DailyStatusDigestResult>;
@@ -0,0 +1,164 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Core: daily status digest email.
4
+ *
5
+ * Emails the previous 24h of order activity to the team every morning. The
6
+ * digest's arrival is itself the platform health signal — if it stops
7
+ * landing, something in the Firestore → Functions → Resend chain needs a
8
+ * look. Deliberately no separate error/exception tracking: there is no
9
+ * structured error-log collection to query, and building one just to fill
10
+ * this email would be scope no one asked for.
11
+ *
12
+ * Runs from a scheduled Firebase Function (not a Vercel route), so the
13
+ * unbounded-ish order scan below is safe against CLAUDE.md Rule #6's 10s
14
+ * sync ceiling — it has the Function's much larger budget.
15
+ */
16
+ import { renderToStaticMarkup } from "react-dom/server";
17
+ import { getAdminDb } from "../../../../providers/db-firebase";
18
+ import { sendEmail } from "../../../../features/contact/email";
19
+ import { siteSettingsRepository } from "../../../../features/admin/repository/site-settings.repository";
20
+ import { ORDER_COLLECTION, OrderStatusValues } from "../../../../features/orders/schemas/firestore";
21
+ import { PRODUCT_COLLECTION, ProductStatusValues } from "../../../../features/products/schemas/firestore";
22
+ import { ORDER_FIELDS, PRODUCT_FIELDS } from "../../../../constants/field-names";
23
+ import { EmailBold, EmailContainer, EmailDivider, EmailDoc, EmailFooter, EmailHeader, EmailRow, } from "../../../../features/email/primitives";
24
+ import { normalizeError } from "../../../../errors/normalize";
25
+ const DAY_MS = 24 * 60 * 60 * 1000;
26
+ /** Statuses that shouldn't count toward revenue for the day. */
27
+ const NON_REVENUE_STATUSES = new Set([
28
+ OrderStatusValues.CANCELLED,
29
+ OrderStatusValues.REFUNDED,
30
+ ]);
31
+ function currency(amount) {
32
+ return `₹${amount.toLocaleString("en-IN", { maximumFractionDigits: 2 })}`;
33
+ }
34
+ /** One "Label: value" line. Uses only email primitives — email clients need
35
+ * inline-styled, table-based markup, which the Email* components provide. */
36
+ function StatLine({ label, value }) {
37
+ return (_jsxs(_Fragment, { children: [label, ": ", _jsx(EmailBold, { children: String(value) }), _jsx("br", {})] }));
38
+ }
39
+ /** Gathers the last-24h numbers. Exported so the admin manual-trigger route can reuse it. */
40
+ export async function collectDailyStatusSummary() {
41
+ const db = getAdminDb();
42
+ const since = new Date(Date.now() - DAY_MS);
43
+ const recentSnap = await db
44
+ .collection(ORDER_COLLECTION)
45
+ .where(ORDER_FIELDS.CREATED_AT, ">=", since)
46
+ .get();
47
+ let revenue = 0;
48
+ const statusBreakdown = {};
49
+ for (const doc of recentSnap.docs) {
50
+ const data = doc.data();
51
+ const status = data.status ?? "unknown";
52
+ statusBreakdown[status] = (statusBreakdown[status] ?? 0) + 1;
53
+ if (!NON_REVENUE_STATUSES.has(status)) {
54
+ revenue += Number(data.totalPrice ?? 0) || 0;
55
+ }
56
+ }
57
+ // Orders still sitting in PENDING beyond the 24h window — the one number
58
+ // here that signals something needs attention rather than just reporting
59
+ // volume.
60
+ const stuckSnap = await db
61
+ .collection(ORDER_COLLECTION)
62
+ .where(ORDER_FIELDS.STATUS, "==", OrderStatusValues.PENDING)
63
+ .where(ORDER_FIELDS.CREATED_AT, "<", since)
64
+ .count()
65
+ .get();
66
+ const activeSnap = await db
67
+ .collection(PRODUCT_COLLECTION)
68
+ .where(PRODUCT_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
69
+ .count()
70
+ .get();
71
+ return {
72
+ orderCount: recentSnap.size,
73
+ revenue,
74
+ statusBreakdown,
75
+ stuckPendingCount: stuckSnap.data().count,
76
+ activeListingCount: activeSnap.data().count,
77
+ };
78
+ }
79
+ function renderDigestHtml(summary, siteName, version) {
80
+ const statusRows = Object.entries(summary.statusBreakdown).sort(([a], [b]) => a.localeCompare(b));
81
+ const title = siteName ? `${siteName} — Daily Status` : "Daily Status";
82
+ return `<!DOCTYPE html>${renderToStaticMarkup(_jsx(EmailDoc, { title: title, children: _jsxs(EmailContainer, { children: [_jsx(EmailHeader, { brandName: siteName }), _jsxs(EmailRow, { children: [version
83
+ ? `Deployment ${version} is live. Activity for the last 24 hours.`
84
+ : "Activity for the last 24 hours.", _jsx("br", {}), _jsx("br", {}), _jsx(StatLine, { label: "Orders placed", value: summary.orderCount }), _jsx(StatLine, { label: "Revenue (excl. cancelled/refunded)", value: currency(summary.revenue) }), _jsx(StatLine, { label: "Active listings", value: summary.activeListingCount }), _jsx(StatLine, { label: "Pending > 24h (needs attention)", value: summary.stuckPendingCount })] }), _jsx(EmailDivider, {}), _jsxs(EmailRow, { children: [_jsx(EmailBold, { children: "Orders by status" }), _jsx("br", {}), _jsx("br", {}), statusRows.length === 0 ? ("No orders in this window.") : (statusRows.map(([status, count]) => (_jsx(StatLine, { label: status, value: count }, status))))] }), _jsx(EmailFooter, { copyright: `© ${new Date().getFullYear()}${siteName ? ` ${siteName}` : ""}. All rights reserved.` })] }) }))}`;
85
+ }
86
+ const DEPLOY_MARKER_COLLECTION = "system";
87
+ const DEPLOY_MARKER_DOC = "deployDigest";
88
+ /**
89
+ * Sends the digest once per deployment version.
90
+ *
91
+ * Called from server startup. On Vercel that means EVERY lambda cold start,
92
+ * not once per deploy — so the version marker below is what makes this a
93
+ * per-deployment event rather than a per-cold-start email storm. The claim
94
+ * runs in a transaction because several cold starts can race on the first
95
+ * request after a deploy; exactly one wins and sends.
96
+ */
97
+ export async function runDeploymentDigest(ctx, version) {
98
+ if (!version)
99
+ return { sent: false, reason: "no_version" };
100
+ const markerRef = getAdminDb().collection(DEPLOY_MARKER_COLLECTION).doc(DEPLOY_MARKER_DOC);
101
+ let claimed = false;
102
+ try {
103
+ await getAdminDb().runTransaction(async (tx) => {
104
+ const snap = await tx.get(markerRef);
105
+ const lastVersion = snap.data()?.lastVersion;
106
+ if (lastVersion === version)
107
+ return;
108
+ tx.set(markerRef, { lastVersion: version, sentAt: new Date() }, { merge: true });
109
+ claimed = true;
110
+ });
111
+ }
112
+ catch (err) {
113
+ void normalizeError(err);
114
+ ctx.logger.warn("deploymentDigest: could not claim version marker — skipping", { version });
115
+ return { sent: false, reason: "claim_failed" };
116
+ }
117
+ if (!claimed)
118
+ return { sent: false, reason: "already_sent_for_version" };
119
+ ctx.logger.info("deploymentDigest: claimed version, sending", { version });
120
+ return runDailyStatusDigest(ctx, version);
121
+ }
122
+ export async function runDailyStatusDigest(ctx, version) {
123
+ const settings = await siteSettingsRepository.getSingleton().catch((err) => {
124
+ void normalizeError(err);
125
+ return null;
126
+ });
127
+ const digestConfig = settings?.emailSettings?.dailyDigest;
128
+ if (digestConfig?.enabled !== true) {
129
+ ctx.logger.info("dailyStatusDigest: disabled in site settings — skipping");
130
+ return { sent: false, reason: "disabled" };
131
+ }
132
+ const recipients = (digestConfig.recipients ?? []).filter((r) => r.trim().length > 0);
133
+ if (recipients.length === 0) {
134
+ ctx.logger.warn("dailyStatusDigest: enabled but no recipients configured — skipping");
135
+ return { sent: false, reason: "no_recipients" };
136
+ }
137
+ const ccRecipients = (digestConfig.ccRecipients ?? []).filter((r) => r.trim().length > 0);
138
+ const summary = await collectDailyStatusSummary();
139
+ // No hardcoded brand fallback — appkit is consumer-agnostic; the site name
140
+ // comes from settings or the consumer's own env var.
141
+ const siteName = settings?.siteName?.trim() || process.env.NEXT_PUBLIC_SITE_NAME?.trim() || "";
142
+ const { error } = await sendEmail({
143
+ to: recipients,
144
+ // Spread rather than `cc: undefined` — SendEmailOptions has a
145
+ // Record<string, JsonValue> index signature that rejects undefined.
146
+ ...(ccRecipients.length > 0 ? { cc: ccRecipients } : {}),
147
+ subject: "Daily Status",
148
+ html: renderDigestHtml(summary, siteName, version),
149
+ text: (version ? `Deployment ${version} is live.\n\n` : "") +
150
+ `Orders placed: ${summary.orderCount}\n` +
151
+ `Revenue: ${currency(summary.revenue)}\n` +
152
+ `Active listings: ${summary.activeListingCount}\n` +
153
+ `Pending > 24h: ${summary.stuckPendingCount}`,
154
+ });
155
+ if (error) {
156
+ ctx.logger.error("dailyStatusDigest: send failed", { error: String(error) });
157
+ throw new Error(`Daily status digest send failed: ${String(error)}`);
158
+ }
159
+ ctx.logger.info("dailyStatusDigest: sent", {
160
+ recipients: recipients.length,
161
+ orderCount: summary.orderCount,
162
+ });
163
+ return { sent: true, summary };
164
+ }
@@ -21,6 +21,8 @@ import { userRepository, sessionRepository, storeRepository, productRepository,
21
21
  import { sendNotification } from "../../../../features/admin/actions/notification-actions";
22
22
  import { isSellerUser } from "../../../../features/auth/role-predicates";
23
23
  import { getAdminAuth } from "../../../../providers/db-firebase";
24
+ import { recordAdminAction } from "../../features/audit-log/actions";
25
+ import { AdminAuditActionValues } from "../../../../features/audit-log/schemas/firestore";
24
26
  async function cascadeAddressClusterBan(uid, banData) {
25
27
  const userAddresses = await addressesRepository.listByOwner("user", uid);
26
28
  const uniqueHashes = [...new Set(userAddresses.map((a) => a.addressHash).filter(Boolean))];
@@ -84,6 +86,15 @@ export async function runHardBanCascade(input, ctx) {
84
86
  hardBanExpiresAt: expiresAt ?? null,
85
87
  ...(fraudOrderId ? { hardBanFraudOrderId: fraudOrderId } : {}),
86
88
  });
89
+ void recordAdminAction({
90
+ actorUid: bannedBy,
91
+ action: AdminAuditActionValues.USER_HARD_BAN,
92
+ targetType: "user",
93
+ targetId: uid,
94
+ targetLabel: target.displayName ?? uid,
95
+ reason,
96
+ metadata: { expiresAt: expiresAt?.toISOString() ?? null, fraudOrderId: fraudOrderId ?? null },
97
+ });
87
98
  // 3. Delete active sessions
88
99
  try {
89
100
  const sessions = await sessionRepository.findActiveByUser(uid);
@@ -1,4 +1,5 @@
1
1
  import type { JobContext } from "../runtime/types";
2
+ export declare const SANDBOX_COLLECTIONS: readonly ["categories", "stores", "products", "blogPosts", "events"];
2
3
  export interface TesterSandboxCleanupOptions {
3
4
  /** true = wipe every isTestData:true doc regardless of testDataExpiresAt (manual force-purge). */
4
5
  force?: boolean;
@@ -1,6 +1,9 @@
1
1
  import { batchDelete, getTestDataRefs } from "../handlers/_helpers";
2
2
  import { BID_FIELDS } from "../../../../constants/field-names";
3
- const SANDBOX_COLLECTIONS = ["categories", "stores", "products", "blogPosts", "events"];
3
+ // Exported so testerSandboxRefresh.ts (the every-4h revert+prune job) reuses
4
+ // the exact same collection scope instead of redefining it — one source of
5
+ // truth for "what counts as sandbox scope."
6
+ export const SANDBOX_COLLECTIONS = ["categories", "stores", "products", "blogPosts", "events"];
4
7
  const BID_CHUNK_SIZE = 30; // Firestore `in` query cap
5
8
  export async function runTesterSandboxCleanup(ctx, opts = {}) {
6
9
  const cutoff = opts.force ? null : new Date();
@@ -0,0 +1,12 @@
1
+ import type { JobContext } from "../runtime/types";
2
+ /**
3
+ * Reverts every live tester-sandbox fixture back to its canonical seeded
4
+ * shape (undoing edits made through admin/seller forms) and prunes any
5
+ * isTestData doc that isn't one of the known fixtures (something a tester
6
+ * created during QA, e.g. a cloned product). Runs every 4 hours — distinct
7
+ * from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
8
+ * `testDataExpiresAt` TTL has actually lapsed and never touches still-live
9
+ * docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
10
+ * the permanent Beyblade catalog real visitors browse is never touched.
11
+ */
12
+ export declare function runTesterSandboxRefresh(ctx: JobContext): Promise<void>;
@@ -0,0 +1,85 @@
1
+ import { batchDelete, getTestDataRefs } from "../handlers/_helpers";
2
+ import { BID_FIELDS } from "../../../../constants/field-names";
3
+ import { SANDBOX_COLLECTIONS } from "./testerSandboxCleanup";
4
+ // Direct imports from each defining module (not the seed-data barrel) —
5
+ // this is internal appkit code, not part of the package's public API surface.
6
+ import { categoriesTesterSeedData } from "../../../../features/tester/seed-data/categories-tester-seed-data";
7
+ import { storesTesterSeedData } from "../../../../features/tester/seed-data/stores-tester-seed-data";
8
+ import { productsTesterSeedData } from "../../../../features/tester/seed-data/products-tester-seed-data";
9
+ import { blogTesterSeedData } from "../../../../features/tester/seed-data/blog-tester-seed-data";
10
+ import { eventsTesterSeedData } from "../../../../features/tester/seed-data/events-tester-seed-data";
11
+ import { bidsTesterSeedData } from "../../../../features/tester/seed-data/bids-tester-seed-data";
12
+ const SEED_BY_COLLECTION = {
13
+ categories: categoriesTesterSeedData,
14
+ stores: storesTesterSeedData,
15
+ products: productsTesterSeedData,
16
+ blogPosts: blogTesterSeedData,
17
+ events: eventsTesterSeedData,
18
+ };
19
+ const BID_CHUNK_SIZE = 30; // Firestore `in` query cap
20
+ /**
21
+ * Reverts every live tester-sandbox fixture back to its canonical seeded
22
+ * shape (undoing edits made through admin/seller forms) and prunes any
23
+ * isTestData doc that isn't one of the known fixtures (something a tester
24
+ * created during QA, e.g. a cloned product). Runs every 4 hours — distinct
25
+ * from `runTesterSandboxCleanup`, which only deletes docs whose 7-day
26
+ * `testDataExpiresAt` TTL has actually lapsed and never touches still-live
27
+ * docs' fields. Scoped to the tester sandbox only (`isTestData: true`) —
28
+ * the permanent Beyblade catalog real visitors browse is never touched.
29
+ */
30
+ export async function runTesterSandboxRefresh(ctx) {
31
+ let reverted = 0;
32
+ let deletedExtras = 0;
33
+ for (const collection of SANDBOX_COLLECTIONS) {
34
+ const seedDocs = SEED_BY_COLLECTION[collection] ?? [];
35
+ const validIds = new Set(seedDocs.map((d) => d.id).filter((id) => Boolean(id)));
36
+ if (seedDocs.length > 0) {
37
+ const batch = ctx.db.batch();
38
+ for (const doc of seedDocs) {
39
+ const { id, ...data } = doc;
40
+ if (!id)
41
+ continue;
42
+ // merge:true mirrors seed-cli.mjs's loadGeneric — the seed payload
43
+ // carries every canonical field, so this fully overwrites anything
44
+ // a tester edited through the UI.
45
+ batch.set(ctx.db.collection(collection).doc(id), data, { merge: true });
46
+ reverted++;
47
+ }
48
+ await batch.commit();
49
+ }
50
+ const testDataRefs = await getTestDataRefs(ctx.db, collection, null);
51
+ const extraRefs = testDataRefs.filter((ref) => !validIds.has(ref.id));
52
+ deletedExtras += await batchDelete(ctx, extraRefs);
53
+ }
54
+ // Bids aren't isTestData-flagged themselves — scoped by productId
55
+ // referencing a sandbox product instead, mirroring runTesterSandboxCleanup's
56
+ // cascade convention.
57
+ const sandboxProductIds = (SEED_BY_COLLECTION.products ?? [])
58
+ .map((d) => d.id)
59
+ .filter((id) => Boolean(id));
60
+ const validBidIds = new Set(bidsTesterSeedData.map((d) => d.id).filter((id) => Boolean(id)));
61
+ if (bidsTesterSeedData.length > 0) {
62
+ const bidBatch = ctx.db.batch();
63
+ for (const bid of bidsTesterSeedData) {
64
+ const { id, ...data } = bid;
65
+ if (!id)
66
+ continue;
67
+ bidBatch.set(ctx.db.collection("bids").doc(id), data, { merge: true });
68
+ reverted++;
69
+ }
70
+ await bidBatch.commit();
71
+ }
72
+ const extraBidRefs = [];
73
+ for (let i = 0; i < sandboxProductIds.length; i += BID_CHUNK_SIZE) {
74
+ const chunk = sandboxProductIds.slice(i, i + BID_CHUNK_SIZE);
75
+ if (chunk.length === 0)
76
+ continue;
77
+ const snap = await ctx.db.collection("bids").where(BID_FIELDS.PRODUCT_ID, "in", chunk).get();
78
+ for (const doc of snap.docs) {
79
+ if (!validBidIds.has(doc.id))
80
+ extraBidRefs.push(doc.ref);
81
+ }
82
+ }
83
+ deletedExtras += await batchDelete(ctx, extraBidRefs);
84
+ ctx.logger.info("Tester sandbox refresh complete", { reverted, deletedExtras });
85
+ }