@mohasinac/appkit 4.30.18 → 4.31.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 (557) hide show
  1. package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +5 -2
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
  3. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  4. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  5. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  6. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -86
  7. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  8. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  9. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
  10. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
  11. package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
  12. package/dist/_internal/server/features/auctions/actions.js +14 -3
  13. package/dist/_internal/server/features/auctions/service.js +1 -1
  14. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  15. package/dist/_internal/server/features/auth/permissions.js +3 -2
  16. package/dist/_internal/server/features/blog/data.js +2 -2
  17. package/dist/_internal/server/features/blog/service.js +3 -3
  18. package/dist/_internal/server/features/brands/data.js +1 -1
  19. package/dist/_internal/server/features/brands/service.js +3 -3
  20. package/dist/_internal/server/features/bundles/actions.js +6 -1
  21. package/dist/_internal/server/features/bundles/data.js +21 -4
  22. package/dist/_internal/server/features/cart/data.js +3 -1
  23. package/dist/_internal/server/features/cart/service.js +13 -2
  24. package/dist/_internal/server/features/categories/data.js +26 -6
  25. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  26. package/dist/_internal/server/features/classified/index.js +0 -1
  27. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  28. package/dist/_internal/server/features/digital-code/index.js +0 -1
  29. package/dist/_internal/server/features/events/actions.js +17 -4
  30. package/dist/_internal/server/features/events/data.js +1 -1
  31. package/dist/_internal/server/features/events/service.js +1 -1
  32. package/dist/_internal/server/features/grouped/data.js +7 -2
  33. package/dist/_internal/server/features/item-requests/data.js +1 -1
  34. package/dist/_internal/server/features/live/index.d.ts +0 -1
  35. package/dist/_internal/server/features/live/index.js +0 -1
  36. package/dist/_internal/server/features/lottery/actions.js +2 -2
  37. package/dist/_internal/server/features/lottery/data.js +1 -1
  38. package/dist/_internal/server/features/orders/actions.js +7 -7
  39. package/dist/_internal/server/features/orders/data.d.ts +1 -1
  40. package/dist/_internal/server/features/orders/data.js +14 -2
  41. package/dist/_internal/server/features/orders/service.js +1 -1
  42. package/dist/_internal/server/features/pre-orders/service.js +1 -1
  43. package/dist/_internal/server/features/prize-draws/actions.js +1 -1
  44. package/dist/_internal/server/features/products/data.js +21 -6
  45. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  46. package/dist/_internal/server/features/products/list-public.js +95 -7
  47. package/dist/_internal/server/features/products/service.js +1 -1
  48. package/dist/_internal/server/features/promotions/actions.js +1 -1
  49. package/dist/_internal/server/features/promotions/data.js +1 -1
  50. package/dist/_internal/server/features/promotions/service.js +1 -1
  51. package/dist/_internal/server/features/reviews/data.d.ts +1 -1
  52. package/dist/_internal/server/features/reviews/data.js +6 -1
  53. package/dist/_internal/server/features/reviews/og-data.js +1 -1
  54. package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
  55. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  56. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  57. package/dist/_internal/server/features/stores/data.js +1 -1
  58. package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
  59. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  60. package/dist/_internal/server/features/tester/visibility.js +2 -2
  61. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  62. package/dist/_internal/server/functions/scheduled.js +27 -1
  63. package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
  64. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  65. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  66. package/dist/_internal/server/jobs/core/index.js +2 -0
  67. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
  68. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  69. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  70. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  71. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  72. package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
  73. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  75. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  77. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  78. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  79. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  80. package/dist/_internal/shared/actions/action-registry.js +69 -22
  81. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  82. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  83. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  84. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  85. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  86. package/dist/_internal/shared/features/layout/types.js +19 -1
  87. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  88. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  89. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  90. package/dist/_internal/shared/features/offers/config.js +73 -2
  91. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  92. package/dist/_internal/shared/features/orders/schema.js +22 -2
  93. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  94. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  95. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  96. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  97. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  98. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  99. package/dist/client/api/useApiQuery.js +17 -6
  100. package/dist/client.d.ts +38 -20
  101. package/dist/client.js +39 -14
  102. package/dist/configs/next.js +10 -2
  103. package/dist/constants/api-endpoints.d.ts +18 -54
  104. package/dist/constants/api-endpoints.js +6 -26
  105. package/dist/constants/field-names.d.ts +6 -24
  106. package/dist/constants/field-names.js +10 -27
  107. package/dist/constants/geo/countries.d.ts +50 -0
  108. package/dist/constants/geo/countries.js +129 -0
  109. package/dist/constants/geo/subdivisions.d.ts +13 -0
  110. package/dist/constants/geo/subdivisions.js +104 -0
  111. package/dist/constants/index.d.ts +2 -2
  112. package/dist/constants/index.js +2 -2
  113. package/dist/constants/table-keys.d.ts +24 -0
  114. package/dist/constants/table-keys.js +28 -0
  115. package/dist/contracts/client-realtime.d.ts +6 -0
  116. package/dist/contracts/extend.d.ts +0 -9
  117. package/dist/contracts/index.d.ts +1 -1
  118. package/dist/contracts/realtime-session.d.ts +12 -12
  119. package/dist/contracts/realtime-session.js +79 -77
  120. package/dist/core/browser.d.ts +1 -1
  121. package/dist/core/browser.js +1 -1
  122. package/dist/core/index.d.ts +1 -1
  123. package/dist/core/index.js +1 -1
  124. package/dist/core/site-config.d.ts +0 -3
  125. package/dist/core/site-config.js +0 -3
  126. package/dist/errors/index.d.ts +1 -1
  127. package/dist/errors/index.js +1 -1
  128. package/dist/errors/messages.d.ts +0 -8
  129. package/dist/errors/messages.js +0 -9
  130. package/dist/features/about/components/DeveloperView.js +7 -2
  131. package/dist/features/about/components/PolicyPageView.js +9 -1
  132. package/dist/features/about/components/PublicProfileView.js +23 -5
  133. package/dist/features/about/schemas/index.d.ts +8 -8
  134. package/dist/features/account/components/AddressFilters.js +32 -11
  135. package/dist/features/account/components/AddressForm.d.ts +26 -25
  136. package/dist/features/account/components/AddressForm.js +81 -103
  137. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  138. package/dist/features/account/components/NotificationBell.js +4 -3
  139. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  140. package/dist/features/account/components/UserOffersPanel.js +1 -1
  141. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  142. package/dist/features/account/components/UserSupportView.js +89 -58
  143. package/dist/features/account/components/index.d.ts +1 -7
  144. package/dist/features/account/components/index.js +0 -3
  145. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  146. package/dist/features/account/hooks/useAddresses.js +4 -6
  147. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  148. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  149. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  150. package/dist/features/account/index.d.ts +0 -3
  151. package/dist/features/account/index.js +0 -3
  152. package/dist/features/account/schemas/index.d.ts +0 -92
  153. package/dist/features/account/schemas/index.js +42 -20
  154. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  155. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  156. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  157. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  158. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  159. package/dist/features/addresses/schemas/address-form.js +212 -0
  160. package/dist/features/addresses/schemas/index.d.ts +1 -0
  161. package/dist/features/addresses/schemas/index.js +8 -0
  162. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  163. package/dist/features/admin/actions/index.d.ts +0 -1
  164. package/dist/features/admin/actions/index.js +0 -1
  165. package/dist/features/admin/actions/notification-actions.js +1 -41
  166. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  167. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  168. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  169. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  170. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  171. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  172. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  173. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  175. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  176. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  177. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  178. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  179. package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
  180. package/dist/features/admin/components/AdminMediaView.js +3 -3
  181. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  182. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  183. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  184. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  185. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  186. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  187. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  188. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  189. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  190. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  191. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  192. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  193. package/dist/features/admin/components/index.d.ts +2 -2
  194. package/dist/features/admin/components/index.js +1 -1
  195. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  196. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  197. package/dist/features/admin/index.d.ts +0 -3
  198. package/dist/features/admin/index.js +0 -2
  199. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  200. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  201. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  202. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  203. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  204. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  205. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  206. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  207. package/dist/features/admin/schemas/firestore.js +15 -151
  208. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  209. package/dist/features/admin/server.d.ts +0 -4
  210. package/dist/features/admin/server.js +0 -2
  211. package/dist/features/admin/types/index.d.ts +0 -7
  212. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  213. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  214. package/dist/features/analytics/types.d.ts +1 -1
  215. package/dist/features/analytics/types.js +13 -0
  216. package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
  217. package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
  218. package/dist/features/auth/actions/index.d.ts +0 -1
  219. package/dist/features/auth/actions/index.js +0 -1
  220. package/dist/features/auth/actions/profile-actions.js +4 -3
  221. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  222. package/dist/features/auth/auth-helpers.js +5 -7
  223. package/dist/features/auth/components/ForgotPasswordView.js +1 -1
  224. package/dist/features/auth/components/Guards.js +2 -1
  225. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  226. package/dist/features/auth/components/LoginForm.js +2 -4
  227. package/dist/features/auth/components/RegisterForm.js +62 -19
  228. package/dist/features/auth/components/ResetPasswordView.js +1 -1
  229. package/dist/features/auth/hooks/useAuth.js +3 -0
  230. package/dist/features/auth/permissions/constants.d.ts +27 -1
  231. package/dist/features/auth/role-predicates.d.ts +75 -7
  232. package/dist/features/auth/role-predicates.js +28 -2
  233. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  234. package/dist/features/auth/schemas/index.d.ts +1 -1
  235. package/dist/features/auth/schemas/index.js +44 -8
  236. package/dist/features/auth/types/index.d.ts +14 -1
  237. package/dist/features/blog/api/route.js +10 -2
  238. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  239. package/dist/features/blog/hooks/useBlog.js +2 -2
  240. package/dist/features/blog/repository/blog.repository.js +3 -1
  241. package/dist/features/blog/schemas/index.d.ts +4 -0
  242. package/dist/features/blog/schemas/index.js +2 -0
  243. package/dist/features/blog/types/index.d.ts +1 -1
  244. package/dist/features/cart/actions/cart-actions.js +13 -4
  245. package/dist/features/cart/utils/pending-ops.js +1 -1
  246. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  247. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  248. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  249. package/dist/features/catalogue/schemas/validation.js +47 -10
  250. package/dist/features/categories/components/BrandDetailPageView.js +28 -12
  251. package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
  252. package/dist/features/categories/repository/categories.repository.js +3 -1
  253. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  254. package/dist/features/categories/schemas/bundle-form.js +149 -0
  255. package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
  256. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
  257. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  258. package/dist/features/classified/components/ClassifiedDetailPageView.js +47 -18
  259. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  260. package/dist/features/consultation/schemas/index.js +34 -10
  261. package/dist/features/corporate/schemas/index.js +36 -15
  262. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
  263. package/dist/features/events/components/EventBanner.js +5 -2
  264. package/dist/features/events/components/EventOfferCard.js +3 -1
  265. package/dist/features/events/components/EventPollWidget.js +1 -1
  266. package/dist/features/events/components/SpinWheelView.js +3 -2
  267. package/dist/features/filters/FilterPanel.js +3 -3
  268. package/dist/features/filters/RangeFilter.d.ts +10 -0
  269. package/dist/features/filters/RangeFilter.js +41 -4
  270. package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
  271. package/dist/features/history/hooks/useHistory.js +29 -27
  272. package/dist/features/homepage/components/AdSlot.js +3 -1
  273. package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
  274. package/dist/features/homepage/components/MarketplaceHomepageView.js +58 -15
  275. package/dist/features/homepage/components/SocialFeedSection.js +4 -1
  276. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  277. package/dist/features/homepage/lib/live-stats.js +8 -1
  278. package/dist/features/homepage/repository/carousel.repository.js +2 -1
  279. package/dist/features/layout/BottomActions.js +84 -11
  280. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  281. package/dist/features/layout/BottomActionsContext.js +1 -1
  282. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  283. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  284. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  285. package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
  286. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  287. package/dist/features/lottery/schemas/config-write.js +69 -9
  288. package/dist/features/media/finalize.js +3 -1
  289. package/dist/features/media/modals/ImageEditor.js +1 -1
  290. package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
  291. package/dist/features/media/modals/VideoTrimModal.js +5 -2
  292. package/dist/features/media/upload/MediaUploadField.js +2 -2
  293. package/dist/features/messages/hooks/useConversation.js +18 -4
  294. package/dist/features/messages/hooks/useConversations.js +25 -8
  295. package/dist/features/orders/components/RefundRequestView.js +5 -2
  296. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  297. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  298. package/dist/features/orders/schemas/index.d.ts +2 -2
  299. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
  300. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  301. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
  302. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  303. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  304. package/dist/features/products/components/MakeOfferButton.js +33 -11
  305. package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
  306. package/dist/features/products/components/ProductDetailActions.js +0 -1
  307. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  308. package/dist/features/products/components/ProductDetailPageView.js +16 -7
  309. package/dist/features/products/components/ShareButton.js +3 -1
  310. package/dist/features/products/constants/action-defs.d.ts +1 -2
  311. package/dist/features/products/constants/action-defs.js +40 -43
  312. package/dist/features/products/repository/loadProductFeatures.js +10 -2
  313. package/dist/features/products/repository/products.repository.d.ts +5 -1
  314. package/dist/features/products/repository/products.repository.js +25 -2
  315. package/dist/features/products/schemas/firestore.d.ts +7 -3
  316. package/dist/features/products/schemas/product-features.validators.js +3 -0
  317. package/dist/features/promotions/components/CouponCard.js +11 -8
  318. package/dist/features/reviews/actions/review-actions.js +8 -1
  319. package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
  320. package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
  321. package/dist/features/scams/actions/scam-actions.d.ts +9 -1
  322. package/dist/features/scams/actions/scam-actions.js +52 -12
  323. package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
  324. package/dist/features/scams/components/SellerTrustBadge.js +13 -0
  325. package/dist/features/scams/repository/scammer.repository.js +2 -1
  326. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  327. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  328. package/dist/features/scams/schemas/report-form.js +18 -3
  329. package/dist/features/search/action-index/derive.d.ts +49 -0
  330. package/dist/features/search/action-index/derive.js +121 -0
  331. package/dist/features/search/action-index/repository.d.ts +42 -0
  332. package/dist/features/search/action-index/repository.js +100 -0
  333. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  334. package/dist/features/search/action-index/settings-entries.js +268 -0
  335. package/dist/features/search/action-index/types.d.ts +81 -0
  336. package/dist/features/search/action-index/types.js +102 -0
  337. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  338. package/dist/features/search/action-index/validate-entry.js +114 -0
  339. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  340. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  341. package/dist/features/search/repository/search.repository.js +3 -1
  342. package/dist/features/seller/actions/offer-actions.js +43 -19
  343. package/dist/features/seller/actions/seller-actions.js +29 -5
  344. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  345. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  346. package/dist/features/seller/components/FulfillmentView.js +5 -2
  347. package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
  348. package/dist/features/seller/components/PrintCenterView.js +3 -1
  349. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  350. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  351. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  352. package/dist/features/seller/components/SellerArtView.js +4 -119
  353. package/dist/features/seller/components/SellerBidsView.js +1 -1
  354. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  355. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  356. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  357. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  358. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  359. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  360. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  361. package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
  362. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  363. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  364. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  365. package/dist/features/seller/components/SellerLiveView.js +4 -126
  366. package/dist/features/seller/components/SellerOrdersView.js +3 -3
  367. package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
  368. package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
  369. package/dist/features/seller/components/SellerProductShell.js +15 -5
  370. package/dist/features/seller/components/SellerProductsView.js +24 -10
  371. package/dist/features/seller/components/SellerReviewsView.js +47 -16
  372. package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
  373. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  374. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  375. package/dist/features/seller/components/SellerStickersView.js +4 -119
  376. package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
  377. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  378. package/dist/features/seller/components/SellerSupportView.js +100 -0
  379. package/dist/features/seller/components/index.d.ts +4 -8
  380. package/dist/features/seller/components/index.js +2 -4
  381. package/dist/features/seller/index.d.ts +0 -1
  382. package/dist/features/seller/index.js +0 -1
  383. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  384. package/dist/features/seller/schemas/coupon-form.js +155 -23
  385. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  386. package/dist/features/seller/schemas/offer-forms.js +28 -12
  387. package/dist/features/shell/FormShell.js +2 -2
  388. package/dist/features/shell/QuickFormDrawer.js +2 -2
  389. package/dist/features/shell/SectionForm.d.ts +19 -1
  390. package/dist/features/shell/SectionForm.js +104 -17
  391. package/dist/features/shell/build-sections.d.ts +23 -0
  392. package/dist/features/shell/build-sections.js +106 -8
  393. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  394. package/dist/features/shell/field-ui-meta.js +14 -7
  395. package/dist/features/shell/form-sections.d.ts +46 -0
  396. package/dist/features/shell/form-sections.js +114 -0
  397. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  398. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  399. package/dist/features/shipments/schemas/validation.js +44 -13
  400. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  401. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  402. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  403. package/dist/features/stores/actions/store-query-actions.js +3 -2
  404. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  405. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  406. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
  408. package/dist/features/support/repository/support.repository.d.ts +21 -0
  409. package/dist/features/support/repository/support.repository.js +63 -0
  410. package/dist/features/support/schemas/firestore.d.ts +27 -2
  411. package/dist/features/support/schemas/firestore.js +2 -0
  412. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  413. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  414. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  415. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  416. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  417. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  418. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  419. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  420. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  421. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +291 -30
  422. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +51 -14
  423. package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
  424. package/dist/http/api-handler.js +2 -1
  425. package/dist/index.d.ts +25 -104
  426. package/dist/index.js +38 -143
  427. package/dist/jobs.d.ts +1 -1
  428. package/dist/jobs.js +1 -1
  429. package/dist/monitoring/browser.d.ts +0 -2
  430. package/dist/monitoring/browser.js +12 -1
  431. package/dist/monitoring/index.d.ts +0 -2
  432. package/dist/monitoring/index.js +0 -1
  433. package/dist/next/api/routeHandler.js +9 -4
  434. package/dist/next/index.d.ts +1 -2
  435. package/dist/next/index.js +0 -1
  436. package/dist/next/middleware/index.d.ts +0 -2
  437. package/dist/next/middleware/index.js +0 -1
  438. package/dist/next/routing/route-map.d.ts +8 -7
  439. package/dist/next/routing/route-map.js +10 -3
  440. package/dist/providers/auth-firebase/session.js +3 -1
  441. package/dist/providers/db-firebase/admin-app-lite.js +8 -8
  442. package/dist/providers/db-firebase/admin.js +27 -13
  443. package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
  444. package/dist/providers/db-firebase/browser-stub.js +6 -11
  445. package/dist/providers/db-firebase/index.d.ts +0 -1
  446. package/dist/providers/db-firebase/index.js +0 -1
  447. package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
  448. package/dist/providers/db-firebase/rtdb-paths.js +15 -6
  449. package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
  450. package/dist/providers/db-firebase/rtdb-url.js +50 -0
  451. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  452. package/dist/providers/firebase-client/realtime.d.ts +20 -0
  453. package/dist/providers/firebase-client/realtime.js +34 -1
  454. package/dist/providers.d.ts +1 -1
  455. package/dist/providers.js +1 -1
  456. package/dist/react/hooks/use-action-dispatch.js +8 -2
  457. package/dist/react/hooks/useFormStatePreservation.js +3 -1
  458. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  459. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  460. package/dist/react/hooks/usePendingFilters.js +5 -3
  461. package/dist/react/hooks/usePendingTable.js +15 -3
  462. package/dist/react/hooks/useRealtimeEvent.js +20 -9
  463. package/dist/repositories/index.d.ts +0 -1
  464. package/dist/repositories/index.js +0 -1
  465. package/dist/schemas/registry.d.ts +197 -232
  466. package/dist/schemas/registry.js +5 -6
  467. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  468. package/dist/security/authorization.d.ts +9 -0
  469. package/dist/security/authorization.js +16 -2
  470. package/dist/security/browser.d.ts +1 -3
  471. package/dist/security/browser.js +5 -2
  472. package/dist/security/index.d.ts +1 -3
  473. package/dist/security/index.js +19 -2
  474. package/dist/security/pii-encrypt.js +16 -0
  475. package/dist/security/pii-schemas.d.ts +0 -14
  476. package/dist/security/pii-schemas.js +7 -15
  477. package/dist/seed/_helpers/category-forest.js +2 -2
  478. package/dist/seed/_helpers/media.d.ts +19 -0
  479. package/dist/seed/_helpers/media.js +75 -0
  480. package/dist/seed/blog-posts-seed-data.js +17 -17
  481. package/dist/seed/carousel-slides-seed-data.js +7 -7
  482. package/dist/seed/cart-seed-data.js +13 -13
  483. package/dist/seed/categories-seed-data.js +10 -10
  484. package/dist/seed/events-seed-data.js +24 -24
  485. package/dist/seed/factories/product.factory.js +4 -4
  486. package/dist/seed/factories/store.factory.js +3 -3
  487. package/dist/seed/factories/user.factory.js +2 -2
  488. package/dist/seed/grouped-listings-seed-data.js +11 -11
  489. package/dist/seed/homepage-sections-seed-data.js +2 -2
  490. package/dist/seed/index.d.ts +0 -1
  491. package/dist/seed/index.js +0 -1
  492. package/dist/seed/manifest.d.ts +1 -1
  493. package/dist/seed/manifest.js +0 -5
  494. package/dist/seed/notifications-seed-data.js +20 -1
  495. package/dist/seed/orders-seed-data.js +2 -2
  496. package/dist/seed/products-art-seed-data.js +16 -16
  497. package/dist/seed/products-auctions-seed-data.js +34 -34
  498. package/dist/seed/products-classifieds-seed-data.js +35 -37
  499. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  500. package/dist/seed/products-live-items-seed-data.js +11 -11
  501. package/dist/seed/products-preorders-seed-data.js +13 -13
  502. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  503. package/dist/seed/products-standard-seed-data.js +54 -54
  504. package/dist/seed/products-stickers-seed-data.js +15 -15
  505. package/dist/seed/reviews-seed-data.js +4 -4
  506. package/dist/seed/scammers-seed-data.js +3 -3
  507. package/dist/seed/site-settings-seed-data.js +11 -24
  508. package/dist/seed/store-extensions-seed-data.js +10 -10
  509. package/dist/seed/stores-seed-data.js +11 -11
  510. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  511. package/dist/seed/support-tickets-seed-data.js +20 -1
  512. package/dist/seed/users-seed-data.js +20 -20
  513. package/dist/server-entry.d.ts +0 -1
  514. package/dist/server-entry.js +0 -1
  515. package/dist/server.d.ts +9 -6
  516. package/dist/server.js +7 -13
  517. package/dist/styles.css +248 -68
  518. package/dist/tailwind-utilities.css +1 -1
  519. package/dist/tokens/themes/default-dark.js +2 -2
  520. package/dist/tokens/themes/default-light.js +2 -2
  521. package/dist/tokens/tokens.css +24 -4
  522. package/dist/ui/components/ActionRow.d.ts +50 -0
  523. package/dist/ui/components/ActionRow.js +53 -0
  524. package/dist/ui/components/ActionRow.style.css +145 -0
  525. package/dist/ui/components/Drawer.d.ts +10 -0
  526. package/dist/ui/components/Drawer.js +2 -1
  527. package/dist/ui/components/FilterDrawer.js +4 -1
  528. package/dist/ui/components/FilterDrawer.style.css +6 -12
  529. package/dist/ui/components/FormActionBar.js +14 -1
  530. package/dist/ui/components/FormActionBar.style.css +12 -11
  531. package/dist/ui/components/ListingLayout.js +8 -1
  532. package/dist/ui/components/ListingLayout.style.css +6 -13
  533. package/dist/ui/components/Modal.d.ts +15 -1
  534. package/dist/ui/components/Modal.js +17 -3
  535. package/dist/ui/components/QuickCreateModal.js +2 -2
  536. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  537. package/dist/ui/components/RecordDetailModal.js +2 -2
  538. package/dist/ui/components/SideDrawer.d.ts +8 -0
  539. package/dist/ui/components/SideDrawer.js +2 -1
  540. package/dist/ui/components/SideDrawer.style.css +6 -3
  541. package/dist/ui/components/SideModal.d.ts +11 -1
  542. package/dist/ui/components/SideModal.js +3 -2
  543. package/dist/ui/components/SideModal.style.css +14 -0
  544. package/dist/ui/components/Typography.style.css +33 -24
  545. package/dist/ui/components/index.style.css +1 -0
  546. package/dist/ui/components/toolbar-collapse.js +1 -2
  547. package/dist/ui/forms/FormShell.style.css +260 -311
  548. package/dist/ui/index.d.ts +2 -0
  549. package/dist/ui/index.js +1 -0
  550. package/dist/utils/index.d.ts +0 -1
  551. package/dist/utils/index.js +0 -1
  552. package/dist/validation/index.d.ts +1 -1
  553. package/dist/validation/index.js +1 -1
  554. package/dist/validation/schemas.d.ts +0 -19
  555. package/dist/validation/schemas.js +8 -19
  556. package/package.json +1 -1
  557. package/scripts/seed-cli.mjs +3 -6
@@ -4,11 +4,19 @@
4
4
  * pages to hand `productFeatures` down as a prop and avoid per-card waterfalls.
5
5
  */
6
6
  import { productFeaturesRepository } from "./product-features.repository";
7
+ import { safeRead } from "../../../errors/safe-read";
7
8
  export async function loadProductFeaturesForStore(storeId) {
9
+ // Feature badges decorate a card; a page without them is still correct, so
10
+ // both reads degrade — but a failure here silently strips "Free shipping" /
11
+ // "Authenticity guaranteed" from every card, which nobody would notice.
8
12
  const [platform, store] = await Promise.all([
9
- productFeaturesRepository.listPlatform().catch(() => []),
13
+ safeRead(() => productFeaturesRepository.listPlatform(), {
14
+ route: "productFeatures", key: "productFeatures.platform", fallback: [],
15
+ }),
10
16
  storeId
11
- ? productFeaturesRepository.listForStore(storeId).catch(() => [])
17
+ ? safeRead(() => productFeaturesRepository.listForStore(storeId), {
18
+ route: "productFeatures", key: "productFeatures.store", fallback: [],
19
+ })
12
20
  : Promise.resolve([]),
13
21
  ]);
14
22
  const seen = new Set();
@@ -281,7 +281,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
281
281
  canFilter: boolean;
282
282
  canSort: boolean;
283
283
  };
284
- "classified.meetupArea": {
284
+ "classified.meetupArea.city": {
285
285
  canFilter: boolean;
286
286
  canSort: boolean;
287
287
  };
@@ -317,6 +317,10 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
317
317
  canFilter: boolean;
318
318
  canSort: boolean;
319
319
  };
320
+ "liveItem.transport.method": {
321
+ canFilter: boolean;
322
+ canSort: boolean;
323
+ };
320
324
  };
321
325
  /**
322
326
  * Virtual filter aliases — callers can pass `f=listingType==auction` for
@@ -452,10 +452,11 @@ export class ProductRepository extends BaseRepository {
452
452
  .update({
453
453
  [PRODUCT_FIELDS.VIEW_COUNT]: increment(1),
454
454
  });
455
+ // Fire-and-forget analytics: a lost view increment changes no rendered
456
+ // content, so it must never break the product read path it rides with.
455
457
  }
456
458
  catch (_err) {
457
459
  void normalizeError(_err);
458
- // View analytics must not break product read path.
459
460
  }
460
461
  }
461
462
  /**
@@ -593,7 +594,22 @@ ProductRepository.SIEVE_FIELDS = {
593
594
  // silently skipped. The toggle did nothing at all until 2026-08-21.
594
595
  shippingPaidBy: { canFilter: true, canSort: false },
595
596
  searchTxt: { canFilter: true, canSort: false },
596
- "classified.meetupArea": { canFilter: true, canSort: false },
597
+ /*
598
+ * `classified.meetupArea.city`, NOT `classified.meetupArea`.
599
+ *
600
+ * `meetupArea` is an OBJECT (`{city, locality, pincode}`), so an equality
601
+ * on the parent could never match anything a user typed. The city facet
602
+ * emits `classified.meetupArea.city` (TYPE_FACET_FIELD), `findField` is an
603
+ * exact-key lookup, and `throwExceptions:false` drops what it cannot
604
+ * resolve — so the /classified city filter rendered, counted toward the
605
+ * badge, and changed nothing. Both halves of Root Cause #62 in one entry:
606
+ * an orphan key that matches no field, beside a real field with no entry.
607
+ *
608
+ * The schema's own docstring already named the right path ("drives the
609
+ * (listingType, classified.meetupArea.city, createdAt) index"), and the
610
+ * store-scoped index has always been on `.city`.
611
+ */
612
+ "classified.meetupArea.city": { canFilter: true, canSort: false },
597
613
  "classified.acceptsShipping": { canFilter: true, canSort: false },
598
614
  "classified.negotiable": { canFilter: true, canSort: false },
599
615
  "digitalCode.codeDeliveryMethod": { canFilter: true, canSort: false },
@@ -602,6 +618,13 @@ ProductRepository.SIEVE_FIELDS = {
602
618
  "liveItem.sex": { canFilter: true, canSort: false },
603
619
  "liveItem.jurisdictionAllowed": { canFilter: true, canSort: false },
604
620
  "liveItem.cites": { canFilter: true, canSort: false },
621
+ /*
622
+ * The live-items transport facet. `StoreLiveItemsListing` has listed
623
+ * `liveTransportMethod` in its FILTER_KEYS — so ticking it inflated the
624
+ * active-filter badge — while the key had no TYPE_FACET_FIELD mapping and
625
+ * this field had no entry, so it never reached Firestore from either end.
626
+ */
627
+ "liveItem.transport.method": { canFilter: true, canSort: false },
605
628
  };
606
629
  /**
607
630
  * Virtual filter aliases — callers can pass `f=listingType==auction` for
@@ -64,11 +64,15 @@ export interface ProductClassifiedMeta {
64
64
  locality?: string;
65
65
  pincode?: string;
66
66
  };
67
- /** How the buyer reaches the seller (chat | phone | both). */
68
- contactMethod?: "chat" | "phone" | "both";
69
67
  /** Seller offers shipping in addition to meetup. */
70
68
  acceptsShipping?: boolean;
71
- /** Price is negotiable — UI hints "₹X (negotiable)". */
69
+ /**
70
+ * Price is negotiable — UI hints "₹X (negotiable)".
71
+ *
72
+ * Its sibling `allowOffers` is what actually gates the offer FLOOR on this
73
+ * type: unset means the buyer may still request to buy, but only at the
74
+ * asking price.
75
+ */
72
76
  negotiable?: boolean;
73
77
  }
74
78
  export interface ProductDigitalCodeMeta {
@@ -110,6 +110,9 @@ export const productFeatureFormSchema = z.object({
110
110
  storeId: annotate(productFeatureAdminCreateSchema.shape.storeId, {
111
111
  section: "scope", order: 4, row: "pair",
112
112
  help: "Only used when scope is \"store\".",
113
+ // Hidden for a global feature, and paired with visibleValues at the
114
+ // payload so a store id chosen and then abandoned is not saved.
115
+ when: (v) => v.scope === "store",
113
116
  }),
114
117
  isActive: annotate(productFeatureAdminCreateSchema.shape.isActive, {
115
118
  section: "visibility", sectionLabel: "Visibility", order: 1, row: "quarter",
@@ -5,7 +5,6 @@ import { useState } from "react";
5
5
  import { Pencil, Trash2, ToggleLeft, ToggleRight } from "lucide-react";
6
6
  import { Badge, BaseListingCard, Button, ConfirmDeleteModal, Div, IconButton, Row, Span, Stack, Text } from "../../../ui";
7
7
  import { useLongPress } from "../../../react/hooks/useLongPress";
8
- import { useFeatureFlags } from "../../../client/api/useFeatureFlags";
9
8
  import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
10
9
  const CLS_TOGGLE_ON = "h-4 w-4 text-success dark:text-success";
11
10
  const CLS_DELETE_BTN = "p-[var(--appkit-space-1-5)] text-[var(--appkit-color-text-muted)] hover:bg-error-surface dark:hover:bg-error-surface hover:text-error dark:hover:text-error transition-colors disabled:opacity-50";
@@ -102,12 +101,14 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
102
101
  const discountLabel = formatDiscount(n, labels);
103
102
  const hasAdminActions = Boolean(onEdit || onToggleActive || onDelete);
104
103
  // Show Claim by default on public surfaces; hide automatically when this
105
- // card is being rendered with admin/CRUD actions wired. Also hidden
106
- // whenever the COUPONS feature is disabled — the claim endpoint 404s in
107
- // that state, and previously that 404 was silently swallowed (see
108
- // handleClaim below) instead of the CTA simply not being offered.
109
- const { flags: featureFlags } = useFeatureFlags();
110
- const showClaim = (hideClaim === undefined ? !hasAdminActions : !hideClaim) && featureFlags.COUPONS === true;
104
+ // card is being rendered with admin/CRUD actions wired.
105
+ //
106
+ // This also consulted the `COUPONS` env feature flag, because the claim
107
+ // endpoint 404'd when it was off. Both flag systems were deleted 2026-08-29 —
108
+ // the flag was `true` in every environment, and the endpoint now answers
109
+ // unconditionally under its own auth — so the fetch that backed this check
110
+ // is gone too, along with a network request on every coupon card render.
111
+ const showClaim = hideClaim === undefined ? !hasAdminActions : !hideClaim;
111
112
  const handleCopy = () => {
112
113
  if (!n.code)
113
114
  return;
@@ -131,10 +132,12 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
131
132
  credentials: "include",
132
133
  body: JSON.stringify({ couponCode: n.code, source: claimSource }),
133
134
  });
135
+ // An anonymous visitor gets a 401 here by design, so a failure cannot
136
+ // be distinguished from the expected case. The redirect below still
137
+ // pre-fills the code, which is what the user actually asked for.
134
138
  }
135
139
  catch (_err) {
136
140
  void normalizeError(_err);
137
- /* anon/network — non-fatal */
138
141
  }
139
142
  if (typeof window !== "undefined") {
140
143
  window.location.href = `/cart?coupon=${encodeURIComponent(n.code)}`;
@@ -10,6 +10,7 @@ import { serverLogger } from "../../../monitoring";
10
10
  import { maskPublicReview } from "../../../security";
11
11
  import { finalizeStagedMediaArray, finalizeStagedMediaUrl, } from "../../media/finalize";
12
12
  import { reviewRepository } from "../repository/reviews.repository";
13
+ import { safeRead } from "../../../errors/safe-read";
13
14
  import { productRepository } from "../../products/repository/products.repository";
14
15
  import { ProductStatusValues } from "../../products/schemas";
15
16
  import { userRepository } from "../../auth/repository/user.repository";
@@ -185,7 +186,13 @@ export async function listReviewsBySeller(sellerId) {
185
186
  }
186
187
  const reviewBatches = await Promise.all(productIds
187
188
  .slice(0, 20)
188
- .map((id) => reviewRepository.findApprovedByProduct(id, 50).catch(() => [])));
189
+ // One product's reviews failing must not empty the whole seller feed —
190
+ // but it silently thins it, so each miss is recorded.
191
+ .map((id) => safeRead(() => reviewRepository.findApprovedByProduct(id, 50), {
192
+ route: "reviews.bySeller",
193
+ key: "reviews.findApprovedByProduct",
194
+ fallback: [],
195
+ })));
189
196
  // Each per-product batch is newest-first, but concatenating them yields product order,
190
197
  // not chronological order — the flattened list has to be re-sorted or callers get an
191
198
  // arbitrarily interleaved "latest reviews" list.
@@ -1,12 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
3
  import { reviewRepository } from "../../../repositories";
4
+ import { safeRead } from "../../../errors/safe-read";
4
5
  import { Container, Div, Grid, Heading, Main, Nav, Section, Span, Text } from "../../../ui";
5
6
  import { ROUTES } from "../../../next";
6
7
  import { ReviewDetailShell } from "./ReviewDetailShell";
7
8
  import { ReviewCard } from "./ReviewsList";
8
9
  export async function ReviewDetailPageView({ id }) {
9
- const doc = await reviewRepository.findById(id).catch(() => null);
10
+ // The review IS this page's subject — a read failure must surface rather than
11
+ // become the same `null` a removed review produces, which the "Review not
12
+ // found" branch below already handles.
13
+ const doc = await reviewRepository.findById(id);
10
14
  const review = doc;
11
15
  // storeId === storeSlug for stores in this project (pure slug IDs)
12
16
  const storeSlug = doc?.storeId ?? null;
@@ -15,8 +19,14 @@ export async function ReviewDetailPageView({ id }) {
15
19
  }
16
20
  const [sameProductDocs, sameStoreDocs] = await Promise.all([
17
21
  // 12 = the 6 rendered below, with headroom for the current review + any filtered out.
18
- reviewRepository.findApprovedByProduct(review.productId, 12).catch(() => []),
19
- storeSlug ? reviewRepository.findApprovedByStore(storeSlug).catch(() => []) : Promise.resolve([]),
22
+ safeRead(() => reviewRepository.findApprovedByProduct(review.productId, 12), {
23
+ route: "/reviews/[id]", key: "review.sameProduct", fallback: [],
24
+ }),
25
+ storeSlug
26
+ ? safeRead(() => reviewRepository.findApprovedByStore(storeSlug), {
27
+ route: "/reviews/[id]", key: "review.sameStore", fallback: [],
28
+ })
29
+ : Promise.resolve([]),
20
30
  ]);
21
31
  const sameProductReviews = sameProductDocs.filter((r) => r.id !== review.id).slice(0, 6);
22
32
  const sameStoreReviews = sameStoreDocs.filter((r) => r.id !== review.id).slice(0, 6);
@@ -80,10 +80,12 @@ export function ReviewDetailShell({ review, storeHref }) {
80
80
  }
81
81
  setVoted(true);
82
82
  localStorage.setItem(storageKey, "1");
83
+ // The button stays un-voted and the count unchanged, so the user can
84
+ // simply press it again — this shell has no toast host of its own, and a
85
+ // failed helpful-vote costs nothing.
83
86
  }
84
87
  catch (_err) {
85
88
  void normalizeError(_err);
86
- // silently fail
87
89
  }
88
90
  finally {
89
91
  setVoting(false);
@@ -30,7 +30,15 @@ export declare function getPublicScammerById(id: string): Promise<ScammerDocumen
30
30
  * Returns null if not found or not verified.
31
31
  */
32
32
  export declare function getScammerProfilePageData(id: string): Promise<ScammerProfilePageData | null>;
33
- export type SellerTrustStatus = "clear" | "flagged";
33
+ /**
34
+ * `unknown` exists because `clear` is a POSITIVE SAFETY CLAIM — it renders as
35
+ * "✓ Verified Safe" next to a seller's name. Every read in
36
+ * `getSellerTrustStatus` used to fail open to `clear`, so a scam-registry
37
+ * outage actively endorsed a seller nobody had checked. Failing open is still
38
+ * right (an outage must not brand a legitimate seller a scammer), but the
39
+ * honest failure state is "we could not check", not "we checked and it's fine".
40
+ */
41
+ export type SellerTrustStatus = "clear" | "flagged" | "unknown";
34
42
  export interface SellerTrustResult {
35
43
  status: SellerTrustStatus;
36
44
  /** seoSlug of matched verified scammer profiles — links to /scams/[slug]. Empty when clear. */
@@ -1,6 +1,7 @@
1
1
  "use server";
2
2
  import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
3
3
  import { scammerRepository } from "../repository/scammer.repository";
4
+ import { safeRead } from "../../../errors/safe-read";
4
5
  import { userRepository } from "../../auth/repository/user.repository";
5
6
  import { storeRepository } from "../../stores/repository/store.repository";
6
7
  import { safeFireAndForget } from "../../../utils/safe-fire-forget";
@@ -39,9 +40,12 @@ export async function listVerifiedScammers(searchParams) {
39
40
  */
40
41
  export async function getPublicScammerById(id) {
41
42
  // Try direct id lookup first; fall back to seoSlug query.
42
- let doc = await scammerRepository.findById(id).catch(() => null);
43
+ // The profile IS the caller's subject, and both lookups already return null
44
+ // for "no such id/slug" — swallowing a read failure into that same null would
45
+ // report a real outage as a missing profile.
46
+ let doc = await scammerRepository.findById(id);
43
47
  if (!doc) {
44
- doc = await scammerRepository.findBySeoSlug(id).catch(() => null);
48
+ doc = await scammerRepository.findBySeoSlug(id);
45
49
  }
46
50
  if (!doc || doc.status !== "verified")
47
51
  return null;
@@ -66,27 +70,63 @@ export async function getScammerProfilePageData(id) {
66
70
  return { scammer, incidents, comments, relatedScammers, similarScamReports };
67
71
  }
68
72
  const TRUST_STATUS_CLEAR = { status: "clear", matchedProfileSlugs: [] };
73
+ const TRUST_STATUS_UNKNOWN = { status: "unknown", matchedProfileSlugs: [] };
74
+ /**
75
+ * Wrap a read so a FAILURE is distinguishable from a legitimate empty result.
76
+ *
77
+ * `safeRead` alone cannot express this: its fallback has the same type as a
78
+ * successful read, so `null` from a failed store lookup and `null` from a store
79
+ * that does not exist are the same value. Tagging the outcome is what lets the
80
+ * caller answer "unknown" instead of "clear".
81
+ */
82
+ async function readTagged(fn, key) {
83
+ return safeRead(async () => ({ ok: true, value: await fn() }), {
84
+ route: "sellerTrust",
85
+ key,
86
+ fallback: { ok: false, value: null },
87
+ });
88
+ }
69
89
  /**
70
90
  * P-12 — resolves a store owner's phone/email against the verified (admin-published)
71
91
  * scammer registry only. Never surfaces "under review" reports to buyers — a report
72
92
  * that hasn't been verified must not damage a seller's storefront reputation.
73
93
  */
74
94
  export async function getSellerTrustStatus(storeId) {
75
- const store = await storeRepository.findById(storeId).catch(() => null);
76
- if (!store?.ownerId)
95
+ // Every read below fails OPEN to "clear" — a deliberate choice, because a
96
+ // registry outage must not brand a legitimate seller a scammer. But "clear"
97
+ // is a positive safety claim, so a failure that produces one has to be
98
+ // recorded rather than inferred from a silent null.
99
+ const store = await readTagged(() => storeRepository.findById(storeId), "sellerTrust.store");
100
+ if (!store.ok)
101
+ return TRUST_STATUS_UNKNOWN;
102
+ if (!store.value?.ownerId)
77
103
  return TRUST_STATUS_CLEAR;
78
- const owner = await userRepository.findById(store.ownerId).catch(() => null);
79
- if (!owner)
104
+ const ownerId = store.value.ownerId;
105
+ const owner = await readTagged(() => userRepository.findById(ownerId), "sellerTrust.owner");
106
+ if (!owner.ok)
107
+ return TRUST_STATUS_UNKNOWN;
108
+ if (!owner.value)
80
109
  return TRUST_STATUS_CLEAR;
110
+ const ownerDoc = owner.value;
81
111
  const lookups = [];
82
- if (owner.phoneNumber)
83
- lookups.push(scammerRepository.findByContactField("phones", owner.phoneNumber));
84
- if (owner.email)
85
- lookups.push(scammerRepository.findByContactField("emails", owner.email));
112
+ if (ownerDoc.phoneNumber) {
113
+ const phone = ownerDoc.phoneNumber;
114
+ lookups.push(() => scammerRepository.findByContactField("phones", phone));
115
+ }
116
+ if (ownerDoc.email) {
117
+ const email = ownerDoc.email;
118
+ lookups.push(() => scammerRepository.findByContactField("emails", email));
119
+ }
86
120
  if (lookups.length === 0)
87
121
  return TRUST_STATUS_CLEAR;
88
- const results = await Promise.all(lookups.map((p) => p.catch(() => [])));
89
- const verified = results.flat().filter((s) => s.status === "verified");
122
+ const results = await Promise.all(lookups.map((fn) => readTagged(fn, "sellerTrust.registryLookup")));
123
+ // A partial answer is not an answer: if ANY registry lookup failed we cannot
124
+ // say the seller is clear, only that we could not check.
125
+ if (results.some((r) => !r.ok))
126
+ return TRUST_STATUS_UNKNOWN;
127
+ const verified = results
128
+ .flatMap((r) => r.value ?? [])
129
+ .filter((s) => s.status === "verified");
90
130
  if (verified.length === 0)
91
131
  return TRUST_STATUS_CLEAR;
92
132
  return {
@@ -13,4 +13,4 @@ export interface SellerTrustBadgeProps {
13
13
  * unrelated homepage-section `TrustBadge` config type in
14
14
  * features/homepage/index.ts (marketing trust-badge icons row).
15
15
  */
16
- export declare function SellerTrustBadge({ trust, className }: SellerTrustBadgeProps): import("react").JSX.Element;
16
+ export declare function SellerTrustBadge({ trust, className }: SellerTrustBadgeProps): import("react").JSX.Element | null;
@@ -13,6 +13,19 @@ import { ROUTES } from "../../../next";
13
13
  * features/homepage/index.ts (marketing trust-badge icons row).
14
14
  */
15
15
  export function SellerTrustBadge({ trust, className = "" }) {
16
+ // 🛑 `unknown` MUST be handled before the flagged fallthrough below, and must
17
+ // never render as "Verified Safe".
18
+ //
19
+ // Every read in getSellerTrustStatus used to fail open to "clear", so a
20
+ // scam-registry outage produced an explicit safety endorsement for a seller
21
+ // nobody had checked. Failing open is still right — an outage must not brand
22
+ // a legitimate seller a scammer — but the honest failure state is "we could
23
+ // not check", which is neither of the two claims this badge otherwise makes.
24
+ // Rendering nothing is deliberate: a neutral badge would still be a statement,
25
+ // and the absence of a badge already means "no verified match" everywhere the
26
+ // registry is not consulted.
27
+ if (trust.status === "unknown")
28
+ return null;
16
29
  if (trust.status === "clear") {
17
30
  return (_jsx(Badge, { variant: "success", className: className, children: "\u2713 Verified Safe" }));
18
31
  }
@@ -122,10 +122,11 @@ class ScammerRepository extends BaseRepository {
122
122
  await this.getCollection()
123
123
  .doc(id)
124
124
  .update({ [SCAMMER_FIELDS.VIEWS]: increment(1) });
125
+ // Fire-and-forget analytics: a lost view increment changes no rendered
126
+ // content, so it must never break the profile read it rides along with.
125
127
  }
126
128
  catch (_err) {
127
129
  void normalizeError(_err);
128
- // Fire-and-forget by design.
129
130
  }
130
131
  }
131
132
  /**
@@ -138,6 +138,20 @@ export interface ScammerDocument extends BaseDocument {
138
138
  * Admin records keep the uid; public page shows "Anonymous".
139
139
  */
140
140
  reportedByAnon: boolean;
141
+ /**
142
+ * The reporter ticked "I confirm this report is truthful and accurate".
143
+ *
144
+ * 🛑 The form has always REQUIRED this — a `z.literal(true)` in
145
+ * `scamReportFormSchema` and a hard gate on submit — and never sent it. On a
146
+ * surface where a false report gets someone listed publicly and has account
147
+ * consequences for the reporter, the attestation is precisely the record you
148
+ * want to keep, and it existed only as a client-side gate that any direct
149
+ * call to the route bypassed entirely.
150
+ *
151
+ * Optional on the document so the rows written before 2026-08-29 stay valid;
152
+ * absent means "not recorded", which is honest, and is NOT the same as false.
153
+ */
154
+ reporterAttested?: boolean;
141
155
  status: ScammerStatus;
142
156
  /** UID of admin/employee who verified or rejected. */
143
157
  verifiedBy?: string;
@@ -342,7 +356,7 @@ export declare const SCAMMER_INDEXED_FIELDS: readonly ["status", "scamType", "sc
342
356
  export declare const DEFAULT_SCAMMER_DATA: Partial<ScammerDocument>;
343
357
  /** Max pending submissions per user — prevents spam flooding. */
344
358
  export declare const MAX_PENDING_SCAMMER_REPORTS_PER_USER = 5;
345
- export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon">;
359
+ export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon" | "reporterAttested">;
346
360
  export type ScammerAdminUpdateInput = Partial<Pick<ScammerDocument, "status" | "verifiedBy" | "verifiedAt" | "verificationNote" | "relatedScammerIds" | "mergedFromIds" | "tags" | "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "isContested">>;
347
361
  export declare const SCAMMER_FIELDS: {
348
362
  readonly ID: "id";
@@ -5,7 +5,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
5
5
  upiIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
6
  emails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
7
7
  scamType: z.ZodString;
8
- scamPlatform: z.ZodOptional<z.ZodString>;
8
+ scamPlatform: z.ZodString;
9
9
  amountLost: z.ZodOptional<z.ZodNumber>;
10
10
  itemInvolved: z.ZodOptional<z.ZodString>;
11
11
  description: z.ZodString;
@@ -15,9 +15,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
15
15
  displayName: string;
16
16
  description: string;
17
17
  scamType: string;
18
+ scamPlatform: string;
18
19
  agreed: true;
19
20
  reportedByAnon?: boolean | undefined;
20
- scamPlatform?: string | undefined;
21
21
  amountLost?: number | undefined;
22
22
  itemInvolved?: string | undefined;
23
23
  phones?: string[] | undefined;
@@ -27,9 +27,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
27
27
  displayName: string;
28
28
  description: string;
29
29
  scamType: string;
30
+ scamPlatform: string;
30
31
  agreed: true;
31
32
  reportedByAnon?: boolean | undefined;
32
- scamPlatform?: string | undefined;
33
33
  amountLost?: number | undefined;
34
34
  itemInvolved?: string | undefined;
35
35
  phones?: string[] | undefined;
@@ -39,9 +39,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
39
39
  displayName: string;
40
40
  description: string;
41
41
  scamType: string;
42
+ scamPlatform: string;
42
43
  agreed: true;
43
44
  reportedByAnon?: boolean | undefined;
44
- scamPlatform?: string | undefined;
45
45
  amountLost?: number | undefined;
46
46
  itemInvolved?: string | undefined;
47
47
  phones?: string[] | undefined;
@@ -51,9 +51,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
51
51
  displayName: string;
52
52
  description: string;
53
53
  scamType: string;
54
+ scamPlatform: string;
54
55
  agreed: true;
55
56
  reportedByAnon?: boolean | undefined;
56
- scamPlatform?: string | undefined;
57
57
  amountLost?: number | undefined;
58
58
  itemInvolved?: string | undefined;
59
59
  phones?: string[] | undefined;
@@ -45,8 +45,17 @@ export const scamReportFormSchema = z
45
45
  scamType: annotate(z.string().trim().min(1, "Choose what kind of scam this was."), {
46
46
  section: "what", sectionLabel: "What happened", sectionRequired: true, quick: true, order: 1, row: "pair",
47
47
  }),
48
- scamPlatform: annotate(z.string().trim().max(120).optional(), {
49
- section: "what", order: 2, row: "pair",
48
+ /*
49
+ * Required, matching `POST /api/scams/reports` (`z.string().min(1)`).
50
+ *
51
+ * It was `.optional()` here, so a report with no platform passed every
52
+ * client check and came back a 400. The page papered over it with a
53
+ * hand-written `if (!form.scamType || !form.scamPlatform)` guard — which is
54
+ * the same drift as the address postal rule: two rules for one field, and
55
+ * the looser one is the client's.
56
+ */
57
+ scamPlatform: annotate(z.string().trim().min(1, "Choose where this happened.").max(120), {
58
+ section: "what", order: 2, row: "pair", kind: "select",
50
59
  }),
51
60
  /*
52
61
  * Coerced and bounded. It arrives from a text input and is rendered on the
@@ -62,10 +71,16 @@ export const scamReportFormSchema = z
62
71
  itemInvolved: annotate(z.string().trim().max(200).optional(), {
63
72
  section: "what", order: 4, row: "pair",
64
73
  }),
74
+ /*
75
+ * 100, matching the route. It was 30 here, so a 31-character description
76
+ * satisfied every client check and came back a 400 — and the page's own
77
+ * hand-written guard used 100, which is the number that was actually
78
+ * enforced. Two rules, three values.
79
+ */
65
80
  description: annotate(z
66
81
  .string()
67
82
  .trim()
68
- .min(30, "Describe what happened in at least 30 characters — this is the evidence.")
83
+ .min(100, "Describe what happened in at least 100 characters — this is the evidence.")
69
84
  .max(5000, "Keep the description under 5000 characters."), { section: "what", order: 5, row: "full", kind: "textarea" }),
70
85
  reportedByAnon: annotate(z.boolean().optional(), {
71
86
  section: "declare", sectionLabel: "Declaration", order: 1, row: "quarter",
@@ -0,0 +1,49 @@
1
+ import type { NavPortal, SidebarNavGroup } from "../../../_internal/shared/features/layout/types";
2
+ import type { ActionIndexEntry } from "./types";
3
+ /** A nav group set, with the portal it belongs to. */
4
+ export interface PortalNavGroups {
5
+ portal: NavPortal;
6
+ /** Human name for the breadcrumb — "Admin", "Seller", "Account". */
7
+ portalLabel: string;
8
+ groups: readonly SidebarNavGroup[];
9
+ }
10
+ /**
11
+ * Every sidebar entry, as an index entry.
12
+ *
13
+ * `sectionPath` is the breadcrumb a result row shows — "Admin › System › Site
14
+ * Settings" — and it is the reason the group title is worth carrying: two
15
+ * portals both have "Orders", and without the path a result list shows the
16
+ * same word twice with no way to tell which one you want.
17
+ */
18
+ export declare function deriveNavEntries(sources: readonly PortalNavGroups[]): ActionIndexEntry[];
19
+ /** One quick action, as the registry stores it. */
20
+ export interface QuickActionSource {
21
+ id: string;
22
+ label: string;
23
+ description?: string;
24
+ keywords?: string[];
25
+ href?: string;
26
+ routeKey?: string;
27
+ iconKey?: string;
28
+ requiredPermission?: string;
29
+ requiredRole?: string;
30
+ requiresAuth?: boolean;
31
+ }
32
+ /**
33
+ * Quick actions, as index entries.
34
+ *
35
+ * 🛑 An action with no resolvable href is DROPPED, not indexed with a blank
36
+ * one. `DASHBOARD_QUICK_ACTION_META`'s `routeKey` is dead — every page
37
+ * re-declares its own `*_QUICK_ACTION_HREFS` map instead — so a registry entry
38
+ * may genuinely have nowhere to go. Indexing it anyway produces a search
39
+ * result that does nothing when clicked, which is worse than not finding it.
40
+ */
41
+ export declare function deriveQuickActionEntries(actions: readonly QuickActionSource[], portal: NavPortal, portalLabel: string, hrefFor: (action: QuickActionSource) => string | undefined): ActionIndexEntry[];
42
+ /**
43
+ * Assemble the static base.
44
+ *
45
+ * Deduplicated by id, first writer winning, so a quick action that shares an
46
+ * id with a nav entry does not produce two rows for one destination — and the
47
+ * nav entry, being passed first, is the one that survives.
48
+ */
49
+ export declare function buildActionIndexBase(...groups: readonly ActionIndexEntry[][]): ActionIndexEntry[];