@mohasinac/appkit 4.10.0 → 4.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) 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/audit-log/actions.d.ts +25 -0
  8. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  9. package/dist/_internal/server/features/brands/actions.js +0 -2
  10. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  11. package/dist/_internal/server/features/brands/og.js +1 -1
  12. package/dist/_internal/server/features/grouped/data.d.ts +4 -0
  13. package/dist/_internal/server/features/grouped/data.js +24 -0
  14. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  15. package/dist/_internal/server/features/grouped/index.js +1 -1
  16. package/dist/_internal/server/features/orders/actions.js +9 -0
  17. package/dist/_internal/server/features/orders/adapters.js +25 -0
  18. package/dist/_internal/server/features/products/data.d.ts +2 -2
  19. package/dist/_internal/server/features/products/data.js +5 -23
  20. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  21. package/dist/_internal/server/functions/scheduled.js +17 -1
  22. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  23. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  24. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  25. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  26. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  27. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  28. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  29. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  30. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  31. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  34. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  35. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  36. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  37. package/dist/_internal/server/notifications/channel-health.js +101 -0
  38. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  39. package/dist/_internal/shared/features/brands/schema.js +0 -1
  40. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  41. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  42. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  43. package/dist/_internal/shared/features/products/to-product-item.d.ts +25 -0
  44. package/dist/_internal/shared/features/products/to-product-item.js +45 -0
  45. package/dist/client.d.ts +21 -6
  46. package/dist/client.js +13 -3
  47. package/dist/constants/api-endpoints.d.ts +6 -9
  48. package/dist/constants/api-endpoints.js +2 -3
  49. package/dist/contracts/client-auth.d.ts +3 -7
  50. package/dist/contracts/email.d.ts +1 -0
  51. package/dist/features/account/components/AddressForm.js +24 -2
  52. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  53. package/dist/features/account/components/BecomeSellerView.js +1 -1
  54. package/dist/features/account/components/NotificationBell.js +1 -1
  55. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  56. package/dist/features/account/components/UserBidsView.js +47 -0
  57. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  58. package/dist/features/account/components/UserOrdersView.js +54 -4
  59. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  60. package/dist/features/account/components/UserReturnsView.js +37 -3
  61. package/dist/features/account/components/index.d.ts +3 -2
  62. package/dist/features/account/components/index.js +1 -0
  63. package/dist/features/admin/actions/admin-actions.js +22 -0
  64. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  65. package/dist/features/admin/actions/notification-actions.js +4 -3
  66. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  67. package/dist/features/admin/components/AdminAdsView.js +86 -89
  68. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  69. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  70. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  71. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  72. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  73. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  74. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  75. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  76. package/dist/features/admin/components/AdminCartsView.js +34 -4
  77. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  78. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  79. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  80. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  81. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  82. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  83. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  84. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  85. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  86. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  87. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  88. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  89. package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
  90. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  91. package/dist/features/admin/components/AdminStoresView.js +7 -0
  92. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  93. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  94. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  95. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  96. package/dist/features/admin/components/AdminUsersView.js +1 -1
  97. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  98. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  99. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  100. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  101. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  102. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  103. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  104. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  105. package/dist/features/admin/components/index.d.ts +8 -1
  106. package/dist/features/admin/components/index.js +4 -0
  107. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  108. package/dist/features/admin/constants/filter-tabs.js +27 -0
  109. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  110. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  111. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  112. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  113. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  114. package/dist/features/admin/schemas/firestore.d.ts +12 -0
  115. package/dist/features/analytics/types.d.ts +1 -1
  116. package/dist/features/analytics/types.js +8 -0
  117. package/dist/features/auctions/schemas/index.d.ts +5 -0
  118. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  119. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  120. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  121. package/dist/features/audit-log/schemas/firestore.js +35 -0
  122. package/dist/features/auth/actions/index.d.ts +0 -1
  123. package/dist/features/auth/actions/index.js +0 -1
  124. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  125. package/dist/features/auth/hooks/useAuth.js +21 -37
  126. package/dist/features/auth/permissions/constants.d.ts +1 -1
  127. package/dist/features/auth/permissions/constants.js +2 -0
  128. package/dist/features/blog/schemas/index.d.ts +32 -0
  129. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  130. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  131. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  132. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  133. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  134. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  135. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  136. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  137. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  138. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  139. package/dist/features/categories/schemas/index.d.ts +24 -3
  140. package/dist/features/categories/schemas/index.js +3 -1
  141. package/dist/features/categories/types/index.d.ts +7 -0
  142. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  143. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  144. package/dist/features/contact/components/ContactForm.js +17 -4
  145. package/dist/features/contact/email.d.ts +1 -0
  146. package/dist/features/contact/email.js +3 -0
  147. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  148. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  149. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  150. package/dist/features/events/components/EventFormDrawer.js +5 -1
  151. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  152. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  153. package/dist/features/events/schemas/index.d.ts +22 -0
  154. package/dist/features/grouped/components/GroupedListingsCarousel.js +4 -1
  155. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +4 -0
  156. package/dist/features/grouped/repository/grouped-listings.repository.js +18 -0
  157. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  158. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  159. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  160. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  161. package/dist/features/layout/TitleBarLayout.js +3 -3
  162. package/dist/features/media/MediaImage.js +39 -3
  163. package/dist/features/media/types/index.d.ts +10 -0
  164. package/dist/features/media/types/index.js +1 -0
  165. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  166. package/dist/features/media/upload/MediaUploadField.js +11 -7
  167. package/dist/features/media/upload/video-poster.d.ts +2 -0
  168. package/dist/features/media/upload/video-poster.js +16 -1
  169. package/dist/features/media/upload/video-poster.test.js +1 -1
  170. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  171. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  172. package/dist/features/orders/components/index.d.ts +2 -0
  173. package/dist/features/orders/components/index.js +1 -0
  174. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  175. package/dist/features/orders/constants/payment-window.js +25 -0
  176. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  177. package/dist/features/orders/repository/orders.repository.js +56 -0
  178. package/dist/features/orders/schemas/index.d.ts +24 -24
  179. package/dist/features/orders/types/index.d.ts +33 -0
  180. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  181. package/dist/features/products/components/BidHistory.js +9 -1
  182. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  183. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  184. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  185. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  186. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  187. package/dist/features/products/components/ProductDetailActions.js +1 -1
  188. package/dist/features/products/components/ProductGrid.js +15 -1
  189. package/dist/features/products/constants/action-defs.d.ts +1 -0
  190. package/dist/features/products/constants/action-defs.js +1 -0
  191. package/dist/features/products/schemas/index.d.ts +5 -0
  192. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  193. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  194. package/dist/features/search/components/SearchView.d.ts +1 -3
  195. package/dist/features/search/components/SearchView.js +1 -1
  196. package/dist/features/seller/components/QuickProductForm.js +2 -1
  197. package/dist/features/seller/components/SellerBidsView.js +66 -107
  198. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  199. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  200. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  201. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  202. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  203. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  204. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  205. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  206. package/dist/features/seller/components/SellerProductShell.js +89 -36
  207. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  208. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  209. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  210. package/dist/features/seller/components/SellerProductsView.js +131 -189
  211. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  212. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  213. package/dist/features/seller/components/SellerShippingView.js +41 -4
  214. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  215. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  216. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  217. package/dist/features/seller/components/index.d.ts +2 -2
  218. package/dist/features/seller/components/index.js +2 -2
  219. package/dist/features/shell/FormShell.d.ts +24 -4
  220. package/dist/features/shell/FormShell.js +12 -6
  221. package/dist/features/shell/QuickFormDrawer.js +67 -12
  222. package/dist/features/shell/StepForm.d.ts +30 -1
  223. package/dist/features/shell/StepForm.js +47 -4
  224. package/dist/features/shell/index.d.ts +1 -1
  225. package/dist/features/shell/index.js +1 -1
  226. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  227. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  228. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  229. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  230. package/dist/features/tester/components/TesterHubView.js +3 -2
  231. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  232. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  233. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +404 -15
  234. package/dist/index.d.ts +15 -3
  235. package/dist/index.js +22 -5
  236. package/dist/next/routing/route-map.d.ts +11 -0
  237. package/dist/next/routing/route-map.js +5 -0
  238. package/dist/providers/email-resend/provider.js +1 -0
  239. package/dist/providers/firebase-client/auth.d.ts +1 -0
  240. package/dist/providers/firebase-client/auth.js +7 -1
  241. package/dist/repositories/index.d.ts +1 -0
  242. package/dist/repositories/index.js +1 -0
  243. package/dist/schemas/registry.d.ts +61 -25
  244. package/dist/security/pii-mask.js +1 -1
  245. package/dist/seed/categories-seed-data.js +69 -0
  246. package/dist/seed/products-auctions-seed-data.js +11 -0
  247. package/dist/seed/products-standard-seed-data.js +93 -4
  248. package/dist/seed/site-settings-seed-data.js +13 -1
  249. package/dist/seed/store-extensions-seed-data.js +1 -1
  250. package/dist/server-entry.d.ts +2 -1
  251. package/dist/server-entry.js +20 -2
  252. package/dist/server.d.ts +7 -2
  253. package/dist/server.js +8 -3
  254. package/dist/styles.css +1 -1
  255. package/dist/tailwind-utilities.css +1 -1
  256. package/dist/ui/components/Form.d.ts +11 -2
  257. package/dist/ui/components/Form.js +4 -3
  258. package/dist/ui/components/FormField.d.ts +3 -1
  259. package/dist/ui/components/FormField.js +22 -4
  260. package/dist/ui/components/ListingLayout.d.ts +1 -2
  261. package/dist/ui/components/ListingLayout.js +1 -1
  262. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  263. package/dist/ui/components/RecordDetailModal.js +8 -0
  264. package/dist/ui/components/SlottedListingView.js +9 -1
  265. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  266. package/dist/ui/forms/FormErrorSummary.js +37 -0
  267. package/dist/ui/forms/FormShell.d.ts +37 -2
  268. package/dist/ui/forms/FormShell.js +37 -7
  269. package/dist/ui/forms/FormShell.style.css +17 -0
  270. package/dist/ui/forms/index.d.ts +2 -0
  271. package/dist/ui/forms/index.js +1 -0
  272. package/dist/ui/index.d.ts +4 -0
  273. package/dist/ui/index.js +5 -0
  274. package/dist/utils/media-url.d.ts +3 -1
  275. package/dist/utils/media-url.js +20 -1
  276. package/dist/validation/schemas.d.ts +4 -4
  277. package/package.json +1 -1
  278. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  279. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  280. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  281. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  282. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  283. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  284. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  285. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  286. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  287. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  288. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  289. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  290. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  291. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  292. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  293. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  294. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  295. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  296. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  297. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  298. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  299. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  300. package/dist/features/auth/consent-otp.d.ts +0 -57
  301. package/dist/features/auth/consent-otp.js +0 -127
  302. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  303. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  304. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  305. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  306. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  307. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  308. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  309. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  310. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  311. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  312. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  313. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  314. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  315. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  316. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  317. package/dist/features/live/components/LiveItemsListView.js +0 -44
  318. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  319. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  320. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  321. package/dist/react/hooks/useVisibleItems.js +0 -59
  322. package/dist/ui/components/TabStrip.d.ts +0 -34
  323. package/dist/ui/components/TabStrip.js +0 -56
  324. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import Image from "next/image";
4
- import { useState } from "react";
4
+ import { useEffect, useRef, useState } from "react";
5
5
  import { Div, Row, Span } from "../../ui";
6
6
  import { ROUNDED_MAP, SHADOW_MAP } from "../../ui/components/surface-tokens";
7
7
  import { resolveMediaUrl } from "../../utils/media-url";
@@ -25,6 +25,13 @@ const FALLBACK_ICONS = {
25
25
  gallery: "📦",
26
26
  avatar: "👤",
27
27
  };
28
+ // A freshly-uploaded media slug can legitimately 404 for a brief window
29
+ // right after upload — the Firestore doc write and the Storage object write
30
+ // don't land atomically (see src/app/api/media/[...slug]/route.ts's own
31
+ // comment on this race). Retry with backoff before latching the fallback
32
+ // icon, instead of failing permanently on the very first request.
33
+ const MAX_LOAD_RETRIES = 3;
34
+ const RETRY_BACKOFF_MS = [600, 1200, 2400];
28
35
  /** Resolve every URL in a comma-separated srcset string through the media proxy. */
29
36
  function resolveSrcSet(input) {
30
37
  return input
@@ -44,15 +51,39 @@ function resolveSrcSet(input) {
44
51
  export function MediaImage({ src, alt, size = "card", priority = false, loading, objectFit = "cover", fallback, className, rounded, shadow, style, sources, onError, }) {
45
52
  const [hasError, setHasError] = useState(false);
46
53
  const [isLoaded, setIsLoaded] = useState(false);
54
+ const [retryCount, setRetryCount] = useState(0);
55
+ const retryTimeoutRef = useRef(null);
47
56
  const icon = fallback ?? FALLBACK_ICONS[size];
48
57
  const fitClass = objectFit === "contain" ? "object-contain" : "object-cover";
49
58
  const resolvedSrc = resolveMediaUrl(src);
59
+ // Reset error/load/retry state whenever the resolved src actually changes.
60
+ // Without this, a component instance that hit a load failure once (e.g.
61
+ // lost the post-upload propagation race above) latches hasError forever,
62
+ // even across a later prop change to a src that would now succeed.
63
+ useEffect(() => {
64
+ setHasError(false);
65
+ setIsLoaded(false);
66
+ setRetryCount(0);
67
+ return () => {
68
+ if (retryTimeoutRef.current) {
69
+ clearTimeout(retryTimeoutRef.current);
70
+ retryTimeoutRef.current = null;
71
+ }
72
+ };
73
+ // eslint-disable-next-line react-hooks/exhaustive-deps
74
+ }, [resolvedSrc]);
50
75
  const wrapperExtra = [
51
76
  rounded ? ROUNDED_MAP[rounded] : "",
52
77
  shadow ? SHADOW_MAP[shadow] : "",
53
78
  className ?? "",
54
79
  ].filter(Boolean).join(" ");
55
80
  const handleError = () => {
81
+ if (retryCount < MAX_LOAD_RETRIES) {
82
+ retryTimeoutRef.current = setTimeout(() => {
83
+ setRetryCount((count) => count + 1);
84
+ }, RETRY_BACKOFF_MS[retryCount]);
85
+ return;
86
+ }
56
87
  setHasError(true);
57
88
  onError?.();
58
89
  };
@@ -62,15 +93,20 @@ export function MediaImage({ src, alt, size = "card", priority = false, loading,
62
93
  const isSvg = resolvedSrc.toLowerCase().endsWith(".svg") ||
63
94
  resolvedSrc.includes("image/svg") ||
64
95
  /[./]svg(\?|$)/i.test(resolvedSrc);
96
+ // Cache-bust the retried request so a browser-cached 404 response isn't
97
+ // silently re-served — the underlying URL is otherwise byte-identical.
98
+ const requestSrc = retryCount > 0
99
+ ? `${resolvedSrc}${resolvedSrc.includes("?") ? "&" : "?"}retry=${retryCount}`
100
+ : resolvedSrc;
65
101
  // Art-directed `<picture>` path — bypasses Next.js `<Image>` because the
66
102
  // browser must pick the matching `<source>` itself. Loading + decoding hints
67
103
  // come from the consumer's preset.
68
104
  if (sources && sources.length > 0) {
69
- return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${wrapperExtra ? ` ${wrapperExtra}` : ""}`, style: style, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsxs("picture", { children: [sources.map((source, index) => (_jsx("source", { srcSet: resolveSrcSet(source.srcSet), type: source.type, media: source.media }, `${source.media ?? ""}-${source.type ?? ""}-${index}`))), _jsx("img", { src: resolvedSrc, alt: alt, loading: loading ?? (priority ? "eager" : "lazy"), decoding: "async",
105
+ return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${wrapperExtra ? ` ${wrapperExtra}` : ""}`, style: style, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsxs("picture", { children: [sources.map((source, index) => (_jsx("source", { srcSet: resolveSrcSet(source.srcSet), type: source.type, media: source.media }, `${source.media ?? ""}-${source.type ?? ""}-${index}`))), _jsx("img", { src: requestSrc, alt: alt, loading: loading ?? (priority ? "eager" : "lazy"), decoding: "async",
70
106
  // catalogued primitive for media (variant catalogue allows the inner
71
107
  // <img> here). Object-fit + className come from typed props.
72
108
  className: `absolute inset-0 w-full h-full ${fitClass}`, onLoad: () => setIsLoaded(true), onError: handleError })] })] }));
73
109
  }
74
- return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${wrapperExtra ? ` ${wrapperExtra}` : ""}`, style: style, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsx(Image, { src: resolvedSrc, alt: alt, fill: true, priority: priority, loading: loading ?? (priority ? "eager" : "lazy"), className: fitClass, sizes: SIZE_HINTS[size], onLoad: () => setIsLoaded(true), onError: () => setHasError(true), unoptimized: isSvg })] }));
110
+ return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${wrapperExtra ? ` ${wrapperExtra}` : ""}`, style: style, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsx(Image, { src: requestSrc, alt: alt, fill: true, priority: priority, loading: loading ?? (priority ? "eager" : "lazy"), className: fitClass, sizes: SIZE_HINTS[size], onLoad: () => setIsLoaded(true), onError: handleError, unoptimized: isSvg })] }));
75
111
  }
76
112
  export default MediaImage;
@@ -12,6 +12,13 @@ export interface MediaField {
12
12
  source?: MediaSource;
13
13
  /** YouTube video ID. Only set when source === "youtube". */
14
14
  youtubeId?: string;
15
+ /**
16
+ * Duration in seconds, for `type: "video"`. Captured automatically for
17
+ * direct uploads (off-DOM `<video>` element, see `video-poster.ts`) — not
18
+ * obtainable client-side for `source: "youtube" | "external"` without a
19
+ * server round-trip, so it stays undefined for those sources.
20
+ */
21
+ duration?: number;
15
22
  }
16
23
  export declare const mediaFieldSchema: z.ZodObject<{
17
24
  url: z.ZodString;
@@ -20,6 +27,7 @@ export declare const mediaFieldSchema: z.ZodObject<{
20
27
  thumbnailUrl: z.ZodOptional<z.ZodString>;
21
28
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
22
29
  youtubeId: z.ZodOptional<z.ZodString>;
30
+ duration: z.ZodOptional<z.ZodNumber>;
23
31
  }, "strip", z.ZodTypeAny, {
24
32
  type: "video" | "image" | "file";
25
33
  url: string;
@@ -27,6 +35,7 @@ export declare const mediaFieldSchema: z.ZodObject<{
27
35
  alt?: string | undefined;
28
36
  thumbnailUrl?: string | undefined;
29
37
  youtubeId?: string | undefined;
38
+ duration?: number | undefined;
30
39
  }, {
31
40
  type: "video" | "image" | "file";
32
41
  url: string;
@@ -34,6 +43,7 @@ export declare const mediaFieldSchema: z.ZodObject<{
34
43
  alt?: string | undefined;
35
44
  thumbnailUrl?: string | undefined;
36
45
  youtubeId?: string | undefined;
46
+ duration?: number | undefined;
37
47
  }>;
38
48
  export declare function coerceMediaField(value: MediaFieldInput, fallbackType?: MediaFieldType): MediaField | null;
39
49
  export declare function coerceMediaFieldArray(values: Array<MediaField | string> | null | undefined, fallbackType?: MediaFieldType): MediaField[];
@@ -7,6 +7,7 @@ export const mediaFieldSchema = z.object({
7
7
  thumbnailUrl: z.string().optional(),
8
8
  source: z.enum(["upload", "youtube", "external"]).optional(),
9
9
  youtubeId: z.string().optional(),
10
+ duration: z.number().optional(),
10
11
  });
11
12
  export function coerceMediaField(value, fallbackType = "image") {
12
13
  if (!value) {
@@ -28,6 +28,12 @@ export interface MediaUploadFieldProps {
28
28
  enableTrim?: boolean;
29
29
  enableThumbnail?: boolean;
30
30
  onThumbnailChange?: (url: string) => void;
31
+ /**
32
+ * Fired with the captured duration (seconds) for a direct video upload —
33
+ * mirrors `onThumbnailChange`. Never fires for the YouTube/External-URL
34
+ * tabs (no client-side way to read those without a server round-trip).
35
+ */
36
+ onDurationChange?: (duration: number) => void;
31
37
  /** Show a "YouTube URL" tab to embed a YouTube video by ID or URL. */
32
38
  showYoutube?: boolean;
33
39
  /** Show an "External URL" tab to link a third-party image/video URL. */
@@ -63,4 +69,4 @@ export interface MediaUploadFieldProps {
63
69
  */
64
70
  autoCapturePoster?: boolean;
65
71
  }
66
- export declare function MediaUploadField({ label, value, onChange, onChangeField, onUpload, kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled, helperText, captureSource, captureMode, enableTrim, enableThumbnail, onThumbnailChange, showYoutube, showExternal, onAbort, onStagedUrlsChange, isPersisted, multiple, onAddUrl, autoCapturePoster, }: MediaUploadFieldProps): import("react").JSX.Element;
72
+ export declare function MediaUploadField({ label, value, onChange, onChangeField, onUpload, kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled, helperText, captureSource, captureMode, enableTrim, enableThumbnail, onThumbnailChange, onDurationChange, showYoutube, showExternal, onAbort, onStagedUrlsChange, isPersisted, multiple, onAddUrl, autoCapturePoster, }: MediaUploadFieldProps): import("react").JSX.Element;
@@ -88,7 +88,7 @@ export function MediaUploadField({ label, value, onChange, onChangeField, onUplo
88
88
  // SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
89
89
  // explicitly; explicit props still win. "*" + 50 stay as the floor for
90
90
  // back-compat with callers that pass neither.
91
- kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText, captureSource = "both", captureMode = "photo", enableTrim = true, enableThumbnail = true, onThumbnailChange, showYoutube = true, showExternal = true, onAbort, onStagedUrlsChange, isPersisted = false, multiple = false, onAddUrl, autoCapturePoster = true, }) {
91
+ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText, captureSource = "both", captureMode = "photo", enableTrim = true, enableThumbnail = true, onThumbnailChange, onDurationChange, showYoutube = true, showExternal = true, onAbort, onStagedUrlsChange, isPersisted = false, multiple = false, onAddUrl, autoCapturePoster = true, }) {
92
92
  const [isLoading, setIsLoading] = useState(false);
93
93
  const [error, setError] = useState(null);
94
94
  const [pendingVideoUrl, setPendingVideoUrl] = useState(null);
@@ -198,14 +198,18 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
198
198
  : captureMode === "both"
199
199
  ? "image/*,video/*"
200
200
  : "image/*";
201
- const afterUpload = (url, fileType, autoPosterUrl) => {
201
+ const afterUpload = (url, fileType, autoPosterUrl, duration) => {
202
202
  onChangeField?.({
203
203
  url,
204
204
  type: inferMediaTypeFromMime(fileType, url),
205
+ source: "upload",
205
206
  ...(autoPosterUrl ? { thumbnailUrl: autoPosterUrl } : {}),
207
+ ...(duration ? { duration } : {}),
206
208
  });
207
209
  if (autoPosterUrl)
208
210
  onThumbnailChange?.(autoPosterUrl);
211
+ if (duration)
212
+ onDurationChange?.(duration);
209
213
  const isVideoFile = fileType.startsWith("video/");
210
214
  if (isVideoFile && enableTrim) {
211
215
  setPendingVideoUrl(url);
@@ -283,7 +287,7 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
283
287
  const posterFile = posterBlobAsFile(poster, file.name);
284
288
  const posterUrl = await onUpload(posterFile);
285
289
  stageUrl(posterUrl);
286
- return posterUrl;
290
+ return { posterUrl, duration: poster.duration };
287
291
  }
288
292
  catch (err) {
289
293
  // The upload-poster step is best-effort. Log via normalizeError so any
@@ -309,8 +313,8 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
309
313
  else {
310
314
  const result = await uploadSingleFile(files[0]);
311
315
  if (result) {
312
- const posterUrl = (await maybeCaptureVideoPoster(files[0])) ?? undefined;
313
- afterUpload(result.url, result.type, posterUrl);
316
+ const poster = await maybeCaptureVideoPoster(files[0]);
317
+ afterUpload(result.url, result.type, poster?.posterUrl, poster?.duration);
314
318
  }
315
319
  }
316
320
  }
@@ -353,8 +357,8 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
353
357
  try {
354
358
  const url = await onUpload(file);
355
359
  stageUrl(url);
356
- const posterUrl = (await maybeCaptureVideoPoster(file)) ?? undefined;
357
- afterUpload(url, blob.type || mimeType, posterUrl);
360
+ const poster = await maybeCaptureVideoPoster(file);
361
+ afterUpload(url, blob.type || mimeType, poster?.posterUrl, poster?.duration);
358
362
  }
359
363
  catch (err) {
360
364
  void normalizeError(err);
@@ -26,6 +26,8 @@ export interface ExtractedPoster {
26
26
  /** Frame dimensions in pixels (also the JPEG's pixel dimensions). */
27
27
  width: number;
28
28
  height: number;
29
+ /** Video duration in seconds, read from the same off-DOM element. */
30
+ duration: number;
29
31
  }
30
32
  /**
31
33
  * Build a deterministic filename for the captured poster. We mirror the
@@ -25,6 +25,20 @@ const TARGET_FRAME_TIME_SECONDS = 0.5;
25
25
  const JPEG_QUALITY = 0.85;
26
26
  const LOAD_TIMEOUT_MS = 10000;
27
27
  const POSTER_MIME = "image/jpeg";
28
+ /** Bounded wait for `video.duration` to become a finite, positive number. */
29
+ async function readDuration(video) {
30
+ if (Number.isFinite(video.duration) && video.duration > 0)
31
+ return video.duration;
32
+ return new Promise((resolve) => {
33
+ const timer = window.setTimeout(() => resolve(0), 2000);
34
+ video.addEventListener("durationchange", () => {
35
+ if (Number.isFinite(video.duration) && video.duration > 0) {
36
+ window.clearTimeout(timer);
37
+ resolve(video.duration);
38
+ }
39
+ }, { once: true });
40
+ });
41
+ }
28
42
  /**
29
43
  * Build a deterministic filename for the captured poster. We mirror the
30
44
  * source video's basename so downstream code can correlate the two assets
@@ -86,6 +100,7 @@ export async function extractVideoPosterFrame(file) {
86
100
  const height = video.videoHeight;
87
101
  if (!width || !height)
88
102
  return null;
103
+ const duration = await readDuration(video);
89
104
  const canvas = document.createElement("canvas");
90
105
  canvas.width = width;
91
106
  canvas.height = height;
@@ -96,7 +111,7 @@ export async function extractVideoPosterFrame(file) {
96
111
  const blob = await new Promise((resolve) => canvas.toBlob(resolve, POSTER_MIME, JPEG_QUALITY));
97
112
  if (!blob)
98
113
  return null;
99
- return { blob, width, height };
114
+ return { blob, width, height, duration };
100
115
  }
101
116
  catch (_err) {
102
117
  void normalizeError(_err);
@@ -19,7 +19,7 @@ describe("posterBlobAsFile", () => {
19
19
  const blob = new Blob([new Uint8Array([0xff, 0xd8, 0xff])], {
20
20
  type: "image/jpeg",
21
21
  });
22
- const file = posterBlobAsFile({ blob, width: 320, height: 180 }, "promo-reel.mp4");
22
+ const file = posterBlobAsFile({ blob, width: 320, height: 180, duration: 12.5 }, "promo-reel.mp4");
23
23
  expect(file).toBeInstanceOf(File);
24
24
  expect(file.name).toBe("promo-reel.poster.jpg");
25
25
  expect(file.type).toBe("image/jpeg");
@@ -0,0 +1,12 @@
1
+ export interface OrderStatusTimelineProps {
2
+ orderStatus: string;
3
+ orderDate?: string;
4
+ shippingDate?: string;
5
+ deliveryDate?: string;
6
+ cancellationDate?: string;
7
+ trackingNumber?: string;
8
+ shippingCarrier?: string;
9
+ trackingUrl?: string;
10
+ className?: string;
11
+ }
12
+ export declare function OrderStatusTimeline({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className, }: OrderStatusTimelineProps): import("react").JSX.Element;
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * OrderStatusTimeline — a genuine (not fabricated/estimated) status timeline
5
+ * built from the real per-status timestamps already written by the
6
+ * order-status-update path (`orderDate`/`shippingDate`/`deliveryDate`/
7
+ * `cancellationDate`). No live courier-API integration — `trackingUrl` is
8
+ * just the passthrough link a seller/admin entered, rendered as an external
9
+ * link that opens in a new tab. Statuses with no dedicated date field
10
+ * (confirmed/processing/return_requested/returned/refunded) render as a step
11
+ * without a timestamp rather than showing a made-up date.
12
+ */
13
+ import { Anchor, Badge, Div, Heading, Row, Stack, Text } from "../../../ui";
14
+ const STAGE_SEQUENCE = ["pending", "confirmed", "processing", "shipped", "delivered"];
15
+ const STAGE_LABELS = {
16
+ pending: "Order placed",
17
+ confirmed: "Confirmed",
18
+ processing: "Processing",
19
+ shipped: "Shipped",
20
+ delivered: "Delivered",
21
+ cancelled: "Cancelled",
22
+ return_requested: "Return requested",
23
+ returned: "Returned",
24
+ refunded: "Refunded",
25
+ };
26
+ function buildSteps(props) {
27
+ const status = (props.orderStatus ?? "").toLowerCase();
28
+ const currentIndex = STAGE_SEQUENCE.indexOf(status);
29
+ const steps = [{ key: "pending", label: STAGE_LABELS.pending, date: props.orderDate }];
30
+ if ((status === "confirmed" || status === "processing") && !props.shippingDate) {
31
+ steps.push({ key: status, label: STAGE_LABELS[status] });
32
+ }
33
+ if (props.shippingDate || currentIndex >= 3) {
34
+ steps.push({ key: "shipped", label: STAGE_LABELS.shipped, date: props.shippingDate });
35
+ }
36
+ if (props.deliveryDate || status === "delivered") {
37
+ steps.push({ key: "delivered", label: STAGE_LABELS.delivered, date: props.deliveryDate });
38
+ }
39
+ if (status === "cancelled" || props.cancellationDate) {
40
+ steps.push({ key: "cancelled", label: STAGE_LABELS.cancelled, date: props.cancellationDate });
41
+ }
42
+ else if (status === "return_requested" || status === "returned" || status === "refunded") {
43
+ steps.push({ key: status, label: STAGE_LABELS[status] ?? status });
44
+ }
45
+ return steps;
46
+ }
47
+ export function OrderStatusTimeline({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate, trackingNumber, shippingCarrier, trackingUrl, className = "", }) {
48
+ const steps = buildSteps({ orderStatus, orderDate, shippingDate, deliveryDate, cancellationDate });
49
+ const isTerminalNegative = steps[steps.length - 1]?.key === "cancelled";
50
+ return (_jsxs(Div, { className: className, rounded: "xl", padding: "md", surface: "muted", border: "default", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", className: "mb-3", children: "Tracking" }), _jsx(Stack, { gap: "none", children: steps.map((step, i) => {
51
+ const isLast = i === steps.length - 1;
52
+ const isNegative = step.key === "cancelled";
53
+ return (_jsxs(Row, { gap: "sm", padding: isLast ? "none" : "b-md", children: [_jsxs(Stack, { gap: "none", align: "center", className: "shrink-0", children: [_jsx(Div, { className: `h-2.5 w-2.5 ${isNegative ? "" : "bg-[var(--appkit-color-primary)]"}`, rounded: "full", surface: isNegative ? "danger-surface" : "none" }), !isLast && _jsx(Div, { className: "w-px flex-1 min-h-[24px]", surface: "subtle" })] }), _jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", weight: "medium", className: isNegative ? "text-error" : undefined, children: step.label }), _jsx(Text, { size: "xs", color: "muted", children: step.date ? new Date(step.date).toLocaleString() : "—" })] })] }, step.key));
54
+ }) }), (trackingNumber || shippingCarrier || trackingUrl) && !isTerminalNegative && (_jsx(Div, { className: "mt-3 border-t border-[var(--appkit-color-border)]", padding: "t-sm", children: _jsxs(Stack, { gap: "xs", children: [shippingCarrier && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Carrier" }), _jsx(Text, { size: "sm", weight: "medium", children: shippingCarrier })] })), trackingNumber && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Tracking number" }), _jsx(Text, { size: "sm", weight: "medium", children: trackingNumber })] })), trackingUrl && (_jsx(Row, { justify: "end", children: _jsx(Anchor, { href: trackingUrl, tone: "brand", weight: "semibold", size: "sm", children: "Track with carrier \u2192" }) }))] }) })), !trackingNumber && !trackingUrl && !isTerminalNegative && (_jsx(Badge, { variant: "default", className: "mt-3", children: "No tracking details yet" }))] }));
55
+ }
@@ -6,6 +6,8 @@ export { OrderSiblingPayments } from "./OrderSiblingPayments";
6
6
  export type { OrderSiblingPaymentsProps } from "./OrderSiblingPayments";
7
7
  export { OrderPaymentSummary } from "./OrderPaymentSummary";
8
8
  export type { OrderPaymentSummaryProps } from "./OrderPaymentSummary";
9
+ export { OrderStatusTimeline } from "./OrderStatusTimeline";
10
+ export type { OrderStatusTimelineProps } from "./OrderStatusTimeline";
9
11
  export { RefundHistoryTable } from "./RefundHistoryTable";
10
12
  export type { RefundHistoryTableProps } from "./RefundHistoryTable";
11
13
  export { RefundRequestView } from "./RefundRequestView";
@@ -3,5 +3,6 @@ export { MarketplaceOrderCard, } from "./MarketplaceOrderCard";
3
3
  export { OrderFilters, ORDER_FILTER_KEYS, ORDER_ADMIN_SORT_OPTIONS, ORDER_SELLER_SORT_OPTIONS, ORDER_USER_SORT_OPTIONS, getOrderFilterKeys, getOrderSortOptions, } from "./OrderFilters";
4
4
  export { OrderSiblingPayments } from "./OrderSiblingPayments";
5
5
  export { OrderPaymentSummary } from "./OrderPaymentSummary";
6
+ export { OrderStatusTimeline } from "./OrderStatusTimeline";
6
7
  export { RefundHistoryTable } from "./RefundHistoryTable";
7
8
  export { RefundRequestView } from "./RefundRequestView";
@@ -8,3 +8,28 @@ export declare const PAYMENT_WINDOW_MS: number;
8
8
  /** Cancellation reasons this feature writes to `OrderDocument.cancellationReason` (a free-form string field). */
9
9
  export declare const PAYMENT_WINDOW_EXPIRED_REASON = "payment_window_expired";
10
10
  export declare const PAYMENT_FRAUD_REJECTED_REASON = "payment_fraud_rejected";
11
+ /**
12
+ * The `paymentMethod` values that settle out-of-band and therefore require a
13
+ * buyer-uploaded proof + a human review pass. Single source of truth for
14
+ * `attachPaymentProofAction`'s guard, the admin payment-review queue, and the
15
+ * buyer/seller "needs payment proof" UI — previously each site inlined its own
16
+ * `pm !== "cash" && pm !== "upi_manual" && ...` chain and they could drift.
17
+ */
18
+ export declare const MANUAL_PAYMENT_METHODS: readonly string[];
19
+ export declare function isManualPaymentMethod(method: string | undefined | null): boolean;
20
+ /**
21
+ * The two states an unresolved manual payment can be in. Derived at query
22
+ * time from `paymentProofUrl` + `paymentReviewOutcome` rather than stored as
23
+ * its own field — a denormalised mirror would drift the moment any write path
24
+ * forgot it (CLAUDE.md Root Cause Pattern #42).
25
+ */
26
+ export type PaymentReviewQueueMode = "awaiting_proof" | "awaiting_verification";
27
+ export declare const PAYMENT_REVIEW_QUEUE_MODES: readonly PaymentReviewQueueMode[];
28
+ export declare function isPaymentReviewQueueMode(value: string): value is PaymentReviewQueueMode;
29
+ /**
30
+ * Hard bound on the payment-review queue's Firestore scan (Rule #6 — the
31
+ * Vercel 10s sync ceiling). Safe because an order only sits in this queue for
32
+ * 15 minutes (awaiting proof) or 2 hours (awaiting verification) before a
33
+ * scheduled sweep resolves it, so the live set is inherently small.
34
+ */
35
+ export declare const PAYMENT_REVIEW_QUEUE_SCAN_LIMIT = 500;
@@ -8,3 +8,28 @@ export const PAYMENT_WINDOW_MS = PAYMENT_WINDOW_MINUTES * 60 * 1000;
8
8
  /** Cancellation reasons this feature writes to `OrderDocument.cancellationReason` (a free-form string field). */
9
9
  export const PAYMENT_WINDOW_EXPIRED_REASON = "payment_window_expired";
10
10
  export const PAYMENT_FRAUD_REJECTED_REASON = "payment_fraud_rejected";
11
+ /**
12
+ * The `paymentMethod` values that settle out-of-band and therefore require a
13
+ * buyer-uploaded proof + a human review pass. Single source of truth for
14
+ * `attachPaymentProofAction`'s guard, the admin payment-review queue, and the
15
+ * buyer/seller "needs payment proof" UI — previously each site inlined its own
16
+ * `pm !== "cash" && pm !== "upi_manual" && ...` chain and they could drift.
17
+ */
18
+ export const MANUAL_PAYMENT_METHODS = ["cash", "upi_manual", "emi"];
19
+ export function isManualPaymentMethod(method) {
20
+ return !!method && MANUAL_PAYMENT_METHODS.includes(method);
21
+ }
22
+ export const PAYMENT_REVIEW_QUEUE_MODES = [
23
+ "awaiting_proof",
24
+ "awaiting_verification",
25
+ ];
26
+ export function isPaymentReviewQueueMode(value) {
27
+ return PAYMENT_REVIEW_QUEUE_MODES.includes(value);
28
+ }
29
+ /**
30
+ * Hard bound on the payment-review queue's Firestore scan (Rule #6 — the
31
+ * Vercel 10s sync ceiling). Safe because an order only sits in this queue for
32
+ * 15 minutes (awaiting proof) or 2 hours (awaiting verification) before a
33
+ * scheduled sweep resolves it, so the live set is inherently small.
34
+ */
35
+ export const PAYMENT_REVIEW_QUEUE_SCAN_LIMIT = 500;
@@ -3,6 +3,7 @@ import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-fire
3
3
  import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
4
4
  import { type OrderCreateInput, type OrderDocument, type OrderRefundEvent } from "../schemas";
5
5
  import type { OrderStatus, PaymentStatus } from "../types";
6
+ import { type PaymentReviewQueueMode } from "../constants/payment-window";
6
7
  declare class OrderRepository extends BaseRepository<OrderDocument> {
7
8
  constructor();
8
9
  private decryptOrder;
@@ -217,6 +218,37 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
217
218
  ref: DocumentReference;
218
219
  data: OrderDocument;
219
220
  }>>;
221
+ /**
222
+ * Admin/seller payment-review queue — the manual-payment orders that are
223
+ * waiting on somebody. Two modes:
224
+ *
225
+ * - `awaiting_proof` — buyer hasn't uploaded a screenshot yet
226
+ * (the 15-minute `paymentDeadline` window).
227
+ * - `awaiting_verification` — proof submitted, no admin decision recorded
228
+ * yet (the 2-hour auto-approve window).
229
+ *
230
+ * Both refinements — proof presence and manual `paymentMethod` — are done
231
+ * in-memory rather than as Firestore clauses, deliberately:
232
+ *
233
+ * - `paymentProofUrl != null` would silently exclude every doc where the
234
+ * field was never set at all, which is exactly the `awaiting_proof` set
235
+ * (same reasoning as `getExpiredPaymentDeadlines` /
236
+ * `getUnreviewedProofPastDeadline` above).
237
+ * - keeping `paymentMethod` out of the query means this reuses the
238
+ * existing `(status, paymentStatus, createdAt)` composite index instead
239
+ * of needing a new four-field one.
240
+ *
241
+ * The scan is hard-bounded at {@link PAYMENT_REVIEW_QUEUE_SCAN_LIMIT} for
242
+ * the Vercel 10s ceiling. That bound is generous for this queue by
243
+ * construction: an order only sits in it for 15 minutes (awaiting proof) or
244
+ * 2 hours (awaiting verification) before a scheduled sweep resolves it, so
245
+ * the live set is inherently small. `total` therefore counts matches within
246
+ * the scan window, not an unbounded collection-wide count.
247
+ */
248
+ listPaymentReviewQueue(mode: PaymentReviewQueueMode, opts?: {
249
+ page?: number;
250
+ pageSize?: number;
251
+ }): Promise<FirebaseSieveResult<OrderDocument>>;
220
252
  /**
221
253
  * Cloud Functions: find delivered orders eligible for the weekly payout sweep.
222
254
  *
@@ -4,6 +4,7 @@ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../..
4
4
  import { decryptPiiFields, encryptPiiFields, ORDER_PII_FIELDS, } from "../../../security";
5
5
  import { serverTimestamp } from "../../../contracts/field-ops";
6
6
  import { createOrderId, ORDER_COLLECTION, OrderStatusValues, } from "../schemas";
7
+ import { MANUAL_PAYMENT_METHODS, PAYMENT_REVIEW_QUEUE_SCAN_LIMIT, } from "../constants/payment-window";
7
8
  import { ORDER_FIELDS } from "../../../constants/field-names";
8
9
  /**
9
10
  * Statuses that count toward a user's per-product / per-bundle purchase
@@ -340,6 +341,61 @@ class OrderRepository extends BaseRepository {
340
341
  }))
341
342
  .filter((entry) => !!entry.data.paymentProofUrl && !entry.data.paymentReviewOutcome);
342
343
  }
344
+ /**
345
+ * Admin/seller payment-review queue — the manual-payment orders that are
346
+ * waiting on somebody. Two modes:
347
+ *
348
+ * - `awaiting_proof` — buyer hasn't uploaded a screenshot yet
349
+ * (the 15-minute `paymentDeadline` window).
350
+ * - `awaiting_verification` — proof submitted, no admin decision recorded
351
+ * yet (the 2-hour auto-approve window).
352
+ *
353
+ * Both refinements — proof presence and manual `paymentMethod` — are done
354
+ * in-memory rather than as Firestore clauses, deliberately:
355
+ *
356
+ * - `paymentProofUrl != null` would silently exclude every doc where the
357
+ * field was never set at all, which is exactly the `awaiting_proof` set
358
+ * (same reasoning as `getExpiredPaymentDeadlines` /
359
+ * `getUnreviewedProofPastDeadline` above).
360
+ * - keeping `paymentMethod` out of the query means this reuses the
361
+ * existing `(status, paymentStatus, createdAt)` composite index instead
362
+ * of needing a new four-field one.
363
+ *
364
+ * The scan is hard-bounded at {@link PAYMENT_REVIEW_QUEUE_SCAN_LIMIT} for
365
+ * the Vercel 10s ceiling. That bound is generous for this queue by
366
+ * construction: an order only sits in it for 15 minutes (awaiting proof) or
367
+ * 2 hours (awaiting verification) before a scheduled sweep resolves it, so
368
+ * the live set is inherently small. `total` therefore counts matches within
369
+ * the scan window, not an unbounded collection-wide count.
370
+ */
371
+ async listPaymentReviewQueue(mode, opts = {}) {
372
+ const page = Math.max(1, opts.page ?? 1);
373
+ const pageSize = Math.min(50, Math.max(1, opts.pageSize ?? 25));
374
+ const snap = await this.db
375
+ .collection(this.collection)
376
+ .where(ORDER_FIELDS.STATUS, "==", OrderStatusValues.PENDING)
377
+ .where(ORDER_FIELDS.PAYMENT_STATUS, "==", "pending")
378
+ .orderBy(ORDER_FIELDS.CREATED_AT, "desc")
379
+ .limit(PAYMENT_REVIEW_QUEUE_SCAN_LIMIT)
380
+ .get();
381
+ const matches = snap.docs
382
+ .map((d) => this.decryptOrder({ id: d.id, ...d.data() }))
383
+ .filter((order) => MANUAL_PAYMENT_METHODS.includes(order.paymentMethod ?? ""))
384
+ .filter((order) => mode === "awaiting_proof"
385
+ ? !order.paymentProofUrl
386
+ : !!order.paymentProofUrl && !order.paymentReviewOutcome);
387
+ const total = matches.length;
388
+ const totalPages = total === 0 ? 0 : Math.max(1, Math.ceil(total / pageSize));
389
+ const start = (page - 1) * pageSize;
390
+ return {
391
+ items: matches.slice(start, start + pageSize),
392
+ total,
393
+ page,
394
+ pageSize,
395
+ totalPages,
396
+ hasMore: page < totalPages,
397
+ };
398
+ }
343
399
  /**
344
400
  * Cloud Functions: find delivered orders eligible for the weekly payout sweep.
345
401
  *