@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
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { annotate } from "../../shell/field-ui-meta";
2
3
  export const corporateInquiryStatusSchema = z.enum([
3
4
  "pending",
4
5
  "contacted",
@@ -37,23 +38,30 @@ export const corporateInquirySchema = z.object({
37
38
  * Form schema for submitting a corporate inquiry.
38
39
  */
39
40
  export const submitCorporateInquirySchema = z.object({
40
- companyName: z.string().trim().min(2, "A company name is required.").max(160),
41
- contactPerson: z.string().trim().min(2, "Who should we reply to?").max(120),
42
- designation: z.string().trim().max(120).optional(),
43
- email: z
41
+ companyName: annotate(z.string().trim().min(2, "A company name is required.").max(160), {
42
+ section: "who", sectionLabel: "Your company", sectionRequired: true,
43
+ order: 1, row: "pair", label: "Company name",
44
+ }),
45
+ contactPerson: annotate(z.string().trim().min(2, "Who should we reply to?").max(120), {
46
+ section: "who", order: 2, row: "pair", label: "Contact person",
47
+ }),
48
+ designation: annotate(z.string().trim().max(120).optional(), {
49
+ section: "who", order: 3, row: "pair", label: "Designation",
50
+ }),
51
+ email: annotate(z
44
52
  .string()
45
53
  .trim()
46
54
  .toLowerCase()
47
55
  .min(1, "An email address is required.")
48
- .email("That doesn't look like an email address."),
56
+ .email("That doesn't look like an email address."), { section: "who", order: 4, row: "pair", inputType: "email", label: "Email" }),
49
57
  // Indian mobile, tolerant of spacing and a country code. Optional β€” a
50
58
  // reachable email is enough, and demanding a phone number loses enquiries.
51
- phone: z
59
+ phone: annotate(z
52
60
  .string()
53
61
  .trim()
54
62
  .regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
55
63
  .optional()
56
- .or(z.literal("")),
64
+ .or(z.literal("")), { section: "who", order: 5, row: "pair", inputType: "tel", label: "Phone" }),
57
65
  /*
58
66
  * πŸ›‘ COERCED. These were `z.number()`, and this schema's own form sends
59
67
  * strings β€” so every submission carrying a unit count failed to parse the
@@ -62,19 +70,32 @@ export const submitCorporateInquirySchema = z.object({
62
70
  *
63
71
  * A sales reply is priced from these two, and until now both were free text.
64
72
  */
65
- units: z.coerce
73
+ units: annotate(z.coerce
66
74
  .number({ invalid_type_error: "Enter the number of units as a number." })
67
75
  .int("Units must be a whole number.")
68
76
  .positive("At least one unit.")
69
77
  .max(1000000, "That looks like a typo β€” contact us directly for orders this large.")
70
- .optional(),
71
- budgetPerUnit: z.coerce
78
+ .optional(), { section: "pricing", sectionLabel: "What you need", order: 1, row: "pair", kind: "number", label: "Units" }),
79
+ budgetPerUnit: annotate(z.coerce
72
80
  .number({ invalid_type_error: "Enter a per-unit budget as a number." })
73
81
  .min(0, "A budget cannot be negative.")
74
82
  .max(10000000, "That looks like a typo.")
75
- .optional(),
76
- totalBudget: z.coerce.number().min(0).optional(),
77
- deliveryDateRequired: z.string().optional(),
78
- customBranding: z.boolean().default(false),
79
- message: z.string().trim().max(4000).optional(),
83
+ .optional(), { section: "pricing", order: 2, row: "pair", kind: "number", label: "Budget per unit (β‚Ή)" }),
84
+ // Computed from units Γ— budgetPerUnit on the client, so it is shown as a
85
+ // live read-only preview rather than asked for twice.
86
+ totalBudget: annotate(z.coerce.number().min(0).optional(), {
87
+ section: "pricing", order: 3, row: "pair", kind: "number", label: "Total budget (β‚Ή)",
88
+ derived: true, tier: "t1-derive",
89
+ }),
90
+ deliveryDateRequired: annotate(z.string().optional(), {
91
+ section: "schedule", sectionLabel: "Timeline", order: 1, row: "pair", kind: "date",
92
+ label: "Delivery date required by",
93
+ }),
94
+ customBranding: annotate(z.boolean().default(false), {
95
+ section: "schedule", order: 2, row: "full", kind: "toggle", label: "We need custom branding",
96
+ }),
97
+ message: annotate(z.string().trim().max(4000).optional(), {
98
+ section: "detail", sectionLabel: "Anything else", order: 1, row: "full", kind: "textarea",
99
+ label: "Message",
100
+ }),
80
101
  });
@@ -30,7 +30,10 @@ function toDescriptionHtml(raw) {
30
30
  export async function DigitalCodeDetailPageView({ slug, initialProduct, renderPrimaryActions }) {
31
31
  const product = initialProduct !== undefined
32
32
  ? (initialProduct ?? undefined)
33
- : await getDigitalCodeForDetail(slug).catch(() => undefined);
33
+ // The listing IS this page's subject β€” a read failure must surface rather
34
+ // than become the same `undefined` a removed listing produces, which the
35
+ // "Not Found" branch below already handles.
36
+ : await getDigitalCodeForDetail(slug);
34
37
  if (!product) {
35
38
  return (_jsx(Main, { children: _jsx(Section, { padding: "y-5xl", children: _jsx(Container, { size: "md", children: _jsxs(Stack, { align: "start", gap: "md", className: "text-left", children: [_jsx(Heading, { level: 1, size: "2xl", weight: "semibold", color: "primary", children: "Digital Code Listing Not Found" }), _jsx(Text, { color: "muted", children: "The digital code listing you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.DIGITAL_CODES), className: "text-[length:var(--appkit-text-sm)] font-medium text-primary-600 hover:underline", children: "Browse Digital Codes" })] }) }) }) }));
36
39
  }
@@ -13,10 +13,12 @@ export function EventBanner({ storageKey = "dismissed_event_banners", className
13
13
  const stored = sessionStorage.getItem(storageKey);
14
14
  if (stored)
15
15
  setDismissedIds(JSON.parse(stored));
16
+ // sessionStorage is unavailable in private mode and over quota in some
17
+ // embedded browsers. Losing the dismissal list only means the banner
18
+ // reappears β€” nothing an operator can act on, and nothing the user loses.
16
19
  }
17
20
  catch (_err) {
18
21
  void normalizeError(_err);
19
- // ignore session storage failures
20
22
  }
21
23
  setMounted(true);
22
24
  }, [storageKey]);
@@ -38,10 +40,11 @@ export function EventBanner({ storageKey = "dismissed_event_banners", className
38
40
  setDismissedIds(next);
39
41
  try {
40
42
  sessionStorage.setItem(storageKey, JSON.stringify(next));
43
+ // Same as the read above: the banner is already hidden in local state, so
44
+ // a failed persist only means it returns on the next page load.
41
45
  }
42
46
  catch (_err) {
43
47
  void normalizeError(_err);
44
- // ignore
45
48
  }
46
49
  };
47
50
  return (_jsxs(Section, { tone: "accent-banner", className: [
@@ -24,10 +24,12 @@ export function EventOfferCard({ couponCode, offerDescription, title = "Exclusiv
24
24
  await navigator.clipboard.writeText(couponCode);
25
25
  setCopied(true);
26
26
  setTimeout(() => setCopied(false), 2000);
27
+ // Clipboard access is denied outright on insecure origins and by user
28
+ // permission. The button simply never flips to "Copied", and the code is
29
+ // still visible on screen to select by hand.
27
30
  }
28
31
  catch (_err) {
29
32
  void normalizeError(_err);
30
- /* clipboard unavailable β€” leave button as-is */
31
33
  }
32
34
  };
33
35
  return (_jsx(Div, { className: `${__P.p6}`, rounded: "2xl", border: "default", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, weight: "semibold", size: "2xl", children: title }), offerDescription ? (_jsx(Text, { size: "sm", color: "muted", children: offerDescription })) : null] }), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", gap: "3", rounded: "lg", children: [_jsx(Code, { className: "flex-1", weight: "bold", size: "base", color: "primary", children: couponCode }), _jsx(Button, { type: "button", variant: "outline", onClick: copyCode, children: copied ? "Copied!" : "Copy code" })] }), _jsx(Div, { children: _jsx(Link, { href: href, children: _jsx(Button, { variant: "primary", children: "Apply at checkout" }) }) })] }) }));
@@ -53,7 +53,7 @@ export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries
53
53
  }
54
54
  catch (err) {
55
55
  void normalizeError(err);
56
- setError(err instanceof Error ? err.message : "Something went wrong");
56
+ setError("Something went wrong");
57
57
  }
58
58
  finally {
59
59
  setIsLoading(false);
@@ -4,6 +4,7 @@ import { useCallback, useMemo, useState } from "react";
4
4
  import { Button, ClaimCouponButton, Div, Heading, LoginRequiredModal, Row, Span, Stack, Text, useToast } from "../../../ui";
5
5
  import { isAuthError } from "../../../utils/auth-error";
6
6
  import { normalizeError } from "../../../errors/normalize";
7
+ import { toUserMessage } from "../../../errors/error-display-map";
7
8
  const CLS_WHEEL = "relative mx-auto aspect-square w-64 overflow-hidden rounded-full border-4 border-warning bg-[image:var(--appkit-gradient-promotion)]";
8
9
  // audit-content-alignment-ok: self-contained circular spin-wheel game widget, all text centered under the wheel by design
9
10
  const CLS_PRIZE_BOX = "rounded-xl border border-success bg-success-surface dark:border-success p-[var(--appkit-space-4)] text-center";
@@ -55,14 +56,14 @@ export function SpinWheelView({ eventId, prizes, alreadyUsed, initialPrizeId, in
55
56
  }, ANIMATION_MS);
56
57
  }
57
58
  catch (err) {
58
- void normalizeError(err);
59
+ const e = normalizeError(err);
59
60
  setSpinning(false);
60
61
  if (isAuthError(err)) {
61
62
  setShowLoginModal(true);
62
63
  }
63
64
  else {
64
65
  setError(l.errorFallback);
65
- showToast(err instanceof Error ? err.message : l.errorFallback, "error");
66
+ showToast(toUserMessage(e.code, undefined, { fallback: l.errorFallback }), "error");
66
67
  }
67
68
  }
68
69
  }, [disabled, eventId, l.errorFallback, onSpin, showToast]);
@@ -5,6 +5,7 @@ import { Div } from "../../ui";
5
5
  import { FilterFacetSection } from "./FilterFacetSection";
6
6
  import { RangeFilter } from "./RangeFilter";
7
7
  import { SwitchFilter } from "./SwitchFilter";
8
+ import { joinFilterValues, splitFilterValues } from "../../constants/table-keys";
8
9
  export function FilterPanel({ config, table, className = "", }) {
9
10
  const [expandedIndex, setExpandedIndex] = useState(() => config.findIndex((c) => !c.defaultCollapsed));
10
11
  const handleToggle = (idx) => setExpandedIndex((prev) => (prev === idx ? null : idx));
@@ -17,9 +18,8 @@ export function FilterPanel({ config, table, className = "", }) {
17
18
  return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "single", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, vals[0] ?? "") }, `${item.type}-${item.key}`));
18
19
  }
19
20
  case "facet-multi": {
20
- const raw = table.get(item.key);
21
- const selected = raw ? raw.split(",").filter(Boolean) : [];
22
- return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "multi", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, vals.join(",")) }, `${item.type}-${item.key}`));
21
+ const selected = splitFilterValues(table.get(item.key));
22
+ return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "multi", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, joinFilterValues(vals)) }, `${item.type}-${item.key}`));
23
23
  }
24
24
  case "switch": {
25
25
  const checked = table.get(item.key) === "true";
@@ -18,6 +18,16 @@ export interface RangeFilterProps {
18
18
  className?: string;
19
19
  isOpen?: boolean;
20
20
  onToggle?: () => void;
21
+ /**
22
+ * Override the clear behaviour. **Rarely needed** β€” clearing a range is
23
+ * `onMinChange("") + onMaxChange("")`, which is now the default, so the X
24
+ * appears on every range filter without a call site opting in.
25
+ *
26
+ * It used to be required-in-practice: 18 of the 19 call sites passed nothing
27
+ * and therefore rendered no clear button at all, leaving a range you could
28
+ * set and not unset. Pass this only when clearing must ALSO reset something
29
+ * outside the two values (`FilterPanel` clears its own pending map).
30
+ */
21
31
  onClear?: () => void;
22
32
  }
23
33
  export declare function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onMinChange, onMaxChange, type, prefix, minPlaceholder, maxPlaceholder, defaultCollapsed, showSlider, minBound, maxBound, step, className, isOpen: controlledOpen, onToggle, onClear, }: RangeFilterProps): import("react").JSX.Element;
@@ -5,10 +5,41 @@ import { Button, Div, Input, Row, Slider, Span, Stack, Text } from "../../ui";
5
5
  import { cn } from "./filterUtils";
6
6
  const CLS_BADGE_COUNT = "inline-flex items-center justify-center w-5 h-5 rounded-full bg-success-surface dark:bg-success-surface text-success dark:text-success ring-1 ring-success/20";
7
7
  const CLS_CLEAR_BTN = "inline-flex items-center justify-center w-5 h-5 text-[var(--appkit-color-text-muted)] hover:text-error dark:hover:text-error rounded-full transition-colors";
8
+ /**
9
+ * Parse a filter value, distinguishing "not set" from a legitimate zero.
10
+ *
11
+ * πŸ›‘ NOT `parseFloat(v) || bound`. `parseFloat("")` is NaN and falsy, which is
12
+ * the intended case β€” but so is `parseFloat("0")`, so a user asking for a max
13
+ * of 0 got the 500000 bound instead, and the filter they could see in the
14
+ * inputs was not the one being applied.
15
+ */
16
+ function rawNum(value) {
17
+ const n = Number.parseFloat(value);
18
+ return Number.isFinite(n) ? n : null;
19
+ }
8
20
  function DualSlider({ minValue, maxValue, onMinChange, onMaxChange, minBound, maxBound, step = 1, prefix = "", }) {
9
- const minNum = Math.max(minBound, Math.min(parseFloat(minValue) || minBound, maxBound));
10
- const maxNum = Math.min(maxBound, Math.max(parseFloat(maxValue) || maxBound, minBound));
11
- return (_jsxs(Stack, { gap: "sm", className: "w-full", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, minNum] }), _jsx(Span, { size: "xs", color: "muted", children: "-" }), _jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, maxNum] })] }), _jsxs(Stack, { gap: "xs", "aria-hidden": "true", children: [_jsx(Slider, { value: minNum, min: minBound, max: Math.max(minBound, maxNum - step), step: step, onChange: (v) => onMinChange(String(v)) }), _jsx(Slider, { value: maxNum, min: Math.min(maxBound, minNum + step), max: maxBound, step: step, onChange: (v) => onMaxChange(String(v)) })] }), _jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "xs", color: "muted", children: [prefix, minBound] }), _jsxs(Span, { size: "xs", color: "muted", children: [prefix, maxBound] })] })] }));
21
+ const minRaw = rawNum(minValue);
22
+ const maxRaw = rawNum(maxValue);
23
+ /*
24
+ * Display only. An unset side shows its bound so the track reads sensibly β€”
25
+ * but the bound is never what gets WRITTEN, which is the distinction the old
26
+ * `|| bound` collapsed.
27
+ */
28
+ const minNum = Math.min(Math.max(minRaw ?? minBound, minBound), maxBound);
29
+ const maxNum = Math.max(Math.min(maxRaw ?? maxBound, maxBound), minBound);
30
+ /*
31
+ * Each slider spans the FULL range and clamps itself on write.
32
+ *
33
+ * It used to derive its opposite end from the other thumb β€” the min slider's
34
+ * `max` was `maxNum - step` β€” so with max unset, `maxNum` collapsed to the
35
+ * bound, and any later change to one side silently re-scaled and appeared to
36
+ * reset the other. Clamping on write instead of on range means dragging one
37
+ * thumb can never move or clear the other, and a value that would cross is
38
+ * pinned rather than the whole control rescaling underneath the user.
39
+ */
40
+ const writeMin = (v) => onMinChange(String(Math.min(v, maxRaw ?? maxBound)));
41
+ const writeMax = (v) => onMaxChange(String(Math.max(v, minRaw ?? minBound)));
42
+ return (_jsxs(Stack, { gap: "sm", className: "w-full", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, minNum] }), _jsx(Span, { size: "xs", color: "muted", children: "-" }), _jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, maxNum] })] }), _jsxs(Stack, { gap: "xs", "aria-hidden": "true", children: [_jsx(Slider, { value: minNum, min: minBound, max: maxBound, step: step, onChange: writeMin }), _jsx(Slider, { value: maxNum, min: minBound, max: maxBound, step: step, onChange: writeMax })] }), _jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "xs", color: "muted", children: [prefix, minBound] }), _jsxs(Span, { size: "xs", color: "muted", children: [prefix, maxBound] })] })] }));
12
43
  }
13
44
  export function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onMinChange, onMaxChange, type = "number", prefix, minPlaceholder, maxPlaceholder, defaultCollapsed = true, showSlider = false, minBound, maxBound, step = 1, className = "", isOpen: controlledOpen, onToggle, onClear, }) {
14
45
  const isControlled = controlledOpen !== undefined;
@@ -25,6 +56,12 @@ export function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onM
25
56
  minBound !== undefined &&
26
57
  maxBound !== undefined;
27
58
  const hasValue = !!(minValue || maxValue);
59
+ // Clearing a range needs nothing a caller has to supply. See `onClear` above.
60
+ const handleClear = onClear ??
61
+ (() => {
62
+ onMinChange("");
63
+ onMaxChange("");
64
+ });
28
65
  const inputClass = "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20 ";
29
- return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
66
+ return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), hasValue && (_jsx(Button, { type: "button", onClick: handleClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
30
67
  }
@@ -46,6 +46,7 @@ import { apiClient } from "../../../http";
46
46
  import { ADMIN_ENDPOINTS, SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
47
47
  import { ROUTES } from "../../../next/routing/route-map";
48
48
  import { normalizeError } from "../../../errors/normalize";
49
+ import { toUserMessage } from "../../../errors/error-display-map";
49
50
  import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
50
51
  import { groupedListingFormSchema, } from "../schemas/grouped-listing-form";
51
52
  const EMPTY = {
@@ -173,7 +174,7 @@ export function GroupedListingEditorView({ scope, groupId, fixedStoreId, onSaved
173
174
  }
174
175
  catch (err) {
175
176
  const e = normalizeError(err);
176
- showToast(e.message || "Failed to save the group.", "error");
177
+ showToast(toUserMessage(e.code, undefined, { fallback: "Failed to save the group." }), "error");
177
178
  }
178
179
  finally {
179
180
  setSaving(false);
@@ -38,35 +38,28 @@ async function postAuthTrack(args) {
38
38
  headers: { "Content-Type": "application/json" },
39
39
  body: JSON.stringify(args),
40
40
  });
41
+ // Recently-viewed tracking is a side effect of browsing, fired from a
42
+ // debounced timer with no UI attached. A lost entry costs the user nothing
43
+ // and there is no caller in a position to retry or report it.
41
44
  }
42
45
  catch (_err) {
43
46
  void normalizeError(_err);
44
- // best-effort tracking
45
47
  }
46
48
  }
49
+ // Both deletes below are user-initiated and deliberately let failures throw:
50
+ // swallowing them made `remove`/`clear` report success for a DELETE that never
51
+ // landed, and the row would silently return on the next load.
47
52
  async function deleteAuthItem(productId) {
48
- try {
49
- await fetch(ACCOUNT_ENDPOINTS.HISTORY_ITEM(productId), {
50
- method: "DELETE",
51
- credentials: "include",
52
- });
53
- }
54
- catch (_err) {
55
- void normalizeError(_err);
56
- // ignore
57
- }
53
+ await fetch(ACCOUNT_ENDPOINTS.HISTORY_ITEM(productId), {
54
+ method: "DELETE",
55
+ credentials: "include",
56
+ });
58
57
  }
59
58
  async function deleteAuthAll() {
60
- try {
61
- await fetch(ACCOUNT_ENDPOINTS.HISTORY, {
62
- method: "DELETE",
63
- credentials: "include",
64
- });
65
- }
66
- catch (_err) {
67
- void normalizeError(_err);
68
- // ignore
69
- }
59
+ await fetch(ACCOUNT_ENDPOINTS.HISTORY, {
60
+ method: "DELETE",
61
+ credentials: "include",
62
+ });
70
63
  }
71
64
  export function useHistory(userId) {
72
65
  const isAuth = !!userId;
@@ -81,6 +74,9 @@ export function useHistory(userId) {
81
74
  else {
82
75
  setItems(getGuestHistory());
83
76
  }
77
+ // fetchAuthHistory already degrades to [] on its own, and getGuestHistory
78
+ // is a localStorage read that fails only in private mode β€” either way the
79
+ // list stays as it was, which is the correct visible outcome here.
84
80
  }
85
81
  catch (err) {
86
82
  void normalizeError(err);
@@ -131,14 +127,20 @@ export function useHistory(userId) {
131
127
  trackTimers.current.set(key, timer);
132
128
  }, [isAuth]);
133
129
  const remove = useCallback(async (productId) => {
134
- if (isAuth) {
135
- await deleteAuthItem(productId);
136
- await loadItems();
130
+ try {
131
+ if (isAuth) {
132
+ await deleteAuthItem(productId);
133
+ await loadItems();
134
+ }
135
+ else {
136
+ setItems(removeGuestHistoryItem(productId));
137
+ }
137
138
  }
138
- else {
139
- setItems(removeGuestHistoryItem(productId));
139
+ catch (_err) {
140
+ void normalizeError(_err);
141
+ showToast("Failed to remove this item from history.", "error");
140
142
  }
141
- }, [isAuth, loadItems]);
143
+ }, [isAuth, loadItems, showToast]);
142
144
  const clear = useCallback(async () => {
143
145
  try {
144
146
  if (isAuth) {
@@ -16,10 +16,12 @@ function AdsenseAd({ config }) {
16
16
  return;
17
17
  try {
18
18
  (window.adsbygoogle = window.adsbygoogle || []).push({});
19
+ // adsbygoogle is injected by a third-party script that ad blockers and
20
+ // consent walls routinely prevent from loading. The <ins> below simply
21
+ // stays unfilled β€” there is no failure the site can act on.
19
22
  }
20
23
  catch (_err) {
21
24
  void normalizeError(_err);
22
- // AdSense not loaded yet β€” no-op
23
25
  }
24
26
  }, []);
25
27
  return (_jsx(Div, { ref: ref, children: _jsx("ins", { className: "adsbygoogle block", "data-ad-client": config.adsenseClient ?? "", "data-ad-slot": config.adsenseSlot ?? "", "data-ad-format": config.adsenseFormat ?? "auto", "data-full-width-responsive": "true" }) }));
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
+ import { serverLogger } from "../../../monitoring/server-logger";
3
4
  import { Anchor, Div, GRID_MAP, Heading, HandModeRow, Row, Section, Span, Stack, Text } from "../../../ui";
4
5
  import { MediaImage } from "../../media/MediaImage";
5
6
  import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
@@ -60,8 +61,13 @@ export async function GoogleReviewsSection(config) {
60
61
  totalRatings = result.totalRatings;
61
62
  }
62
63
  catch (_err) {
63
- void normalizeError(_err);
64
- // Fail silently β€” empty state shown
64
+ // The section renders its empty state below, which is indistinguishable
65
+ // from a place that genuinely has no reviews β€” and Google Places calls are
66
+ // billed, so a persistent failure is worth an operator seeing.
67
+ serverLogger.warn("homepage.googleReviews: Places fetch failed", {
68
+ placeId,
69
+ error: normalizeError(_err).message,
70
+ });
65
71
  }
66
72
  const gridClass = layout === "carousel"
67
73
  ? "flex gap-[var(--appkit-space-4)] overflow-x-auto pb-[var(--appkit-space-2)] snap-x snap-mandatory"
@@ -3,10 +3,21 @@ import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
3
3
  import { sortBy } from "@mohasinac/appkit";
4
4
  import { Div, Main } from "../../../ui";
5
5
  import { carouselRepository, faqsRepository, siteSettingsRepository } from "../../../repositories";
6
+ import { safeRead } from "../../../errors/safe-read";
6
7
  import { fetchLiveStats } from "../lib/live-stats";
7
8
  import { renderSection, AnnouncementBar } from "../lib/section-renderer";
8
9
  import { homepageSectionsRepository } from "../repository/homepage-sections.repository";
9
- import { filterSectionsByFeatureFlags } from "../../../_internal/shared/features/homepage/section-gate";
10
+ import { isListingTypeEnabled } from "../../../_internal/shared/listing-types/feature-flags";
11
+ /**
12
+ * Homepage sections that advertise a specific listing type, and must therefore
13
+ * disappear when the site stops offering it. A section type absent from this
14
+ * map always renders.
15
+ */
16
+ const HOMEPAGE_SECTION_LISTING_TYPE = {
17
+ auctions: "auction",
18
+ "pre-orders": "pre-order",
19
+ "prize-draws": "prize-draw",
20
+ };
10
21
  import { getFeaturedProducts, getFeaturedAuctions, getFeaturedPreOrders } from "../../products/actions/product-actions";
11
22
  import { listTopLevelCategories, listBrandCategories } from "../../categories/actions/category-actions";
12
23
  import { listFeaturedBundles } from "../../../_internal/server/features/bundles/data";
@@ -28,19 +39,39 @@ function toCarouselSlides(docs) {
28
39
  }));
29
40
  }
30
41
  export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onBannerDismiss, } = {}) {
31
- const slides = await carouselRepository.getActiveSlides().catch(() => []);
32
- const siteSettings = await siteSettingsRepository.getSingleton().catch(() => null);
42
+ // Every read on this page is a RAIL: the homepage must still render when one
43
+ // fails, but a failure has to be distinguishable from an empty catalogue β€”
44
+ // that is exactly Root Cause #59, and safeRead is what records it.
45
+ const slides = await safeRead(() => carouselRepository.getActiveSlides(), {
46
+ route: "/", key: "homepage.carouselSlides", fallback: [],
47
+ });
48
+ const siteSettings = await safeRead(() => siteSettingsRepository.getSingleton(), {
49
+ route: "/", key: "homepage.siteSettings", fallback: null,
50
+ });
33
51
  const announcementMessage = siteSettings?.announcementBar?.message?.trim() ||
34
52
  "πŸŽ‰ Up to 15% Off on PokΓ©mon TCG this week β€” Use code SAVE15";
35
53
  const showAnnouncement = siteSettings?.announcementBar?.enabled ?? true;
36
54
  const [allSections, rawFaqItems] = await Promise.all([
37
- homepageSectionsRepository.getEnabledSections().catch(() => []),
38
- faqsRepository.getHomepageFAQs().catch(() => []),
55
+ safeRead(() => homepageSectionsRepository.getEnabledSections(), {
56
+ route: "/", key: "homepage.enabledSections", fallback: [],
57
+ }),
58
+ safeRead(() => faqsRepository.getHomepageFAQs(), {
59
+ route: "/", key: "homepage.faqs", fallback: [],
60
+ }),
39
61
  ]);
40
- // Respect the feature flags. This gate existed but lived inside a function
41
- // with no call sites, so until 2026-08-24 turning off `auctions` hid auctions
42
- // everywhere EXCEPT the homepage, which went on advertising them.
43
- const enabledSections = filterSectionsByFeatureFlags(allSections, siteSettings?.featureFlags);
62
+ // A homepage section must not advertise a listing type the site does not
63
+ // offer. This gate previously read `featureFlags.{auctions,preOrders,events,
64
+ // blog}`; that group was deleted on 2026-08-29, and only the listing-type
65
+ // half of it had a real successor (`settings.listings.listingTypes`).
66
+ //
67
+ // So auctions / pre-orders / prize-draws are gated by the same control that
68
+ // hides them on every other surface, and the `events` / `blog` sections are
69
+ // now unconditional β€” there is no product control for "we don't run events",
70
+ // and there never was one that anything but this line read.
71
+ const enabledSections = allSections.filter((section) => {
72
+ const gated = HOMEPAGE_SECTION_LISTING_TYPE[section.type];
73
+ return !gated || isListingTypeEnabled(gated, siteSettings);
74
+ });
44
75
  // Collect live metric requests from all enabled stats sections
45
76
  const liveStatRequests = [];
46
77
  for (const section of enabledSections) {
@@ -87,12 +118,24 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
87
118
  activeTypes.has("products") ? getFeaturedProducts(12) : null,
88
119
  activeTypes.has("auctions") ? getFeaturedAuctions(12) : null,
89
120
  activeTypes.has("pre-orders") ? getFeaturedPreOrders(12) : null,
90
- activeTypes.has("categories") ? listTopLevelCategories(12).catch(() => null) : null,
91
- activeTypes.has("brands") ? listBrandCategories(12).catch(() => null) : null,
92
- activeTypes.has("featured-bundles") ? listFeaturedBundles(8).catch(() => null) : null,
93
- activeTypes.has("stores") ? listStores({ pageSize: 8, sorts: sortBy("averageRating", "DESC") }).catch(() => null) : null,
94
- activeTypes.has("blog-articles") ? getFeaturedBlogPosts(6).catch(() => null) : null,
95
- activeTypes.has("events") ? listPublicEvents({ filters: sieveFilter("status", SIEVE_OP.EQ, "active"), pageSize: 6 }).catch(() => null) : null,
121
+ activeTypes.has("categories")
122
+ ? safeRead(() => listTopLevelCategories(12), { route: "/", key: "homepage.categories", fallback: null })
123
+ : null,
124
+ activeTypes.has("brands")
125
+ ? safeRead(() => listBrandCategories(12), { route: "/", key: "homepage.brands", fallback: null })
126
+ : null,
127
+ activeTypes.has("featured-bundles")
128
+ ? safeRead(() => listFeaturedBundles(8), { route: "/", key: "homepage.featuredBundles", fallback: null })
129
+ : null,
130
+ activeTypes.has("stores")
131
+ ? safeRead(() => listStores({ pageSize: 8, sorts: sortBy("averageRating", "DESC") }), { route: "/", key: "homepage.stores", fallback: null })
132
+ : null,
133
+ activeTypes.has("blog-articles")
134
+ ? safeRead(() => getFeaturedBlogPosts(6), { route: "/", key: "homepage.blogPosts", fallback: null })
135
+ : null,
136
+ activeTypes.has("events")
137
+ ? safeRead(() => listPublicEvents({ filters: sieveFilter("status", SIEVE_OP.EQ, "active"), pageSize: 6 }), { route: "/", key: "homepage.events", fallback: null })
138
+ : null,
96
139
  ]);
97
140
  // ProductDocument / BlogDocument / EventDocument have Date fields and extra Firestore-only
98
141
  // fields absent from the corresponding *Item types. The casts below are safe: section
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
+ import { safeRead } from "../../../errors/safe-read";
3
4
  import { Anchor, Div, GRID_MAP, Heading, HandModeRow, Section, Stack, Text } from "../../../ui";
4
5
  import { SocialPostCard } from "./SocialPostCard";
5
6
  import { fetchInstagramPosts, fetchFacebookPosts, fetchTikTokPosts, fetchDeviantArtPosts, } from "../lib/social-feed-fetcher";
@@ -47,7 +48,9 @@ async function loadPosts(config) {
47
48
  if (!handle)
48
49
  return { posts: [], error: `A handle is required for ${PLATFORM_LABELS[platform]}.` };
49
50
  try {
50
- const credentials = await siteSettingsRepository.getDecryptedCredentials().catch(() => null);
51
+ // A failed credentials read is reported below as "token not configured in
52
+ // Site Settings", which is actionable but wrong β€” record the real cause.
53
+ const credentials = await safeRead(() => siteSettingsRepository.getDecryptedCredentials(), { route: "/", key: "homepage.socialFeed.credentials", fallback: null });
51
54
  switch (platform) {
52
55
  case "instagram": {
53
56
  const token = credentials?.metaPageAccessToken;
@@ -23,7 +23,7 @@ export function useBlogArticles(options) {
23
23
  return featuredResult;
24
24
  }
25
25
  const remaining = MIN_BLOG_COUNT - featured.length;
26
- const latestResult = await apiClient.get(`${BLOG_ENDPOINTS.SORTED("-publishedAt")}&perPage=${MIN_BLOG_COUNT + remaining}`);
26
+ const latestResult = await apiClient.get(`${BLOG_ENDPOINTS.SORTED("-publishedAt")}&pageSize=${MIN_BLOG_COUNT + remaining}`);
27
27
  const existingIds = new Set(featured.map((p) => p.id));
28
28
  const filler = latestResult.posts
29
29
  .filter((p) => !existingIds.has(p.id))
@@ -81,7 +81,14 @@ export async function fetchLiveStats(requests) {
81
81
  }
82
82
  catch (_err) {
83
83
  void normalizeError(_err);
84
- // silently fall back to static value
84
+ // Falling back to the admin-authored static value is correct β€” but a
85
+ // stale number that looks live is exactly the failure this must not
86
+ // hide, so name the collection that could not be counted.
87
+ serverLogger.warn("homepage.liveStats: collection count failed", {
88
+ statKey: key,
89
+ collection,
90
+ error: normalizeError(_err).message,
91
+ });
85
92
  }
86
93
  })());
87
94
  }
@@ -234,10 +234,11 @@ export class CarouselRepository extends BaseRepository {
234
234
  "stats.views": increment(1),
235
235
  "stats.lastViewed": new Date(),
236
236
  });
237
+ // Fire-and-forget analytics: a lost view increment changes nothing that
238
+ // renders, and it must never break the carousel read it rides along with.
237
239
  }
238
240
  catch (_e) {
239
241
  void normalizeError(_e);
240
- // Swallow errors β€” analytics failures must not break the carousel response
241
242
  }
242
243
  }
243
244
  }