@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
@@ -33,7 +33,7 @@ import { useState, useRef, useEffect, useId } from "react";
33
33
  import { X, ChevronUp, ChevronDown, Check } from "lucide-react";
34
34
  import { useBottomActionsContext } from "./BottomActionsContext";
35
35
  import { useClickOutside } from "../../react";
36
- import { Div, Row, Span, Text, Button } from "../../ui";
36
+ import { ActionRow, Div, Row, Span, Text, Button } from "../../ui";
37
37
  // Token values inlined from @mohasinac/appkit/tokens
38
38
  const BOTTOM_NAV_BG = "bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)]";
39
39
  const BOTTOM_NAV_HEIGHT = "h-14";
@@ -88,21 +88,94 @@ function InfoPanel({ id, open, children, }) {
88
88
  .filter(Boolean)
89
89
  .join(" "), "aria-hidden": !open, children: _jsx(Div, { className: "max-h-[60vh]", overflow: "y-auto", padding: "md", children: children }) }));
90
90
  }
91
- function PageActionsRow({ pageActions, dispatchAction, }) {
92
- return (_jsx(_Fragment, { children: pageActions.map((action) => {
91
+ /**
92
+ * How many actions fit on one line before the row splits in two.
93
+ *
94
+ * 🛑 ROW COUNT is a function of `pageActions.length` and NOTHING else — never
95
+ * of validation state, and never of measured width. `BottomChrome`'s
96
+ * ResizeObserver faithfully republishes `--bottom-chrome-height` on any height
97
+ * change, and every consumer (BackToTop, the pagination bar, the footer's
98
+ * clearance) reflows with it. A bar that grew a row when an error appeared
99
+ * would shift the whole page while the user was typing. The error count lives
100
+ * in `infoLabel`, which sits in the absolutely-positioned panel and is excluded
101
+ * from the published height by construction.
102
+ *
103
+ * The invariant is about ROW COUNT, not pixel height. `ActionRow` lets a long
104
+ * label wrap to a second line, which changes the bar's height by ~16px,
105
+ * monotonically, and the observer handles that correctly. What must never
106
+ * happen is a row count that FLIPS: that is a ~48px step and it is reversible
107
+ * mid-interaction.
108
+ *
109
+ * Which is exactly why the count split stays primary and `flex-wrap` is only an
110
+ * overflow net. In this app LABELS ARE STATE — "Add to Cart" becomes "Adding…",
111
+ * "Buy Now" becomes "Out of Stock", "Apply" becomes "Apply (3)". If width alone
112
+ * decided rows, tapping Add to Cart would shorten the label, free ~40px,
113
+ * un-wrap a row and shift the page under the user's thumb at the exact moment
114
+ * they committed to the tap.
115
+ *
116
+ * 🛑 This constant is COUPLED to `--appkit-size-action-min` (tokens.css).
117
+ * Two actions per line is safe only while `2 × action-min + gap` fits the
118
+ * narrowest supported viewport (280px). Raising either without re-checking that
119
+ * sum reintroduces device-dependent row flipping.
120
+ */
121
+ const SINGLE_ROW_MAX_ACTIONS = 2;
122
+ function ActionButtons({ actions, dispatchAction, }) {
123
+ return (_jsx(_Fragment, { children: actions.map((action) => {
93
124
  const isIconOnly = !action.label;
94
- const growClass = isIconOnly || action.grow === false
95
- ? "flex-shrink-0 w-11"
96
- : "flex-1 min-w-0";
125
+ /*
126
+ * Width is ActionRow's job now, not this component's — every action is
127
+ * sized from its own label by `.appkit-action-row__group > *`, and only
128
+ * the leftover is shared out. There is deliberately NO per-action width
129
+ * class here: the old `grow` ternary is what produced a 44px stub
130
+ * reading "Ba" next to a full-width "Continue to payment".
131
+ *
132
+ * The one thing a stylesheet cannot infer is that a child has no label,
133
+ * so icon-only opts into its square tap target explicitly.
134
+ *
135
+ * 🛑 Never add `flex-1`, `flex-shrink-0`, `basis-*`, `w-*`, `h-*`,
136
+ * `truncate` or `leading-none` below. Both tailwind configs set
137
+ * `important: true`, so any of them beats ActionRow's CSS and silently
138
+ * reverts this bar to the squeezed behaviour with no visible error.
139
+ */
97
140
  return (_jsxs(Button, { type: "button", variant: action.variant ?? "primary", size: "sm", isLoading: action.loading, disabled: action.disabled, onClick: () => dispatchAction(action.id), className: [
98
- "h-10 relative",
99
- growClass,
100
- isIconOnly ? "px-[var(--appkit-space-0)] justify-center" : "",
141
+ "relative",
142
+ isIconOnly ? "appkit-action-row__icon-only justify-center" : "",
101
143
  ]
102
144
  .filter(Boolean)
103
- .join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { className: "truncate leading-none", children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-[var(--appkit-space-1)] pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
145
+ .join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-[var(--appkit-space-1)] pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
104
146
  }) }));
105
147
  }
148
+ /**
149
+ * Lays a form's or a page's actions out across one or two lines.
150
+ *
151
+ * | n | layout |
152
+ * |------|-----------------------------------------------------------------|
153
+ * | 1 | one row, full-width primary |
154
+ * | 2 | one row, each sized from its own label |
155
+ * | 3+ | two rows — secondaries above, primary full-width below |
156
+ *
157
+ * The LAST action is treated as the primary. That holds for every producer:
158
+ * `useFormBottomActions` appends submit last, and the checkout/cart bars build
159
+ * `[back, primary]`. Putting it on its own line keeps the thumb target the same
160
+ * size no matter how many secondary actions a screen has, which is the whole
161
+ * reason a 5-action form (3 forms + soft-ban + hard-ban on the admin user
162
+ * editor) was unrepresentable before.
163
+ *
164
+ * Secondaries WRAP rather than overflowing into a menu. Wrapping keeps the
165
+ * height a pure function of the action count — the invariant above — whereas a
166
+ * popover would add a second interaction to reach an action that is already
167
+ * only one line away. No current caller exceeds five.
168
+ */
169
+ function PageActionsRow({ pageActions, dispatchAction, }) {
170
+ if (pageActions.length === 0)
171
+ return null;
172
+ if (pageActions.length <= SINGLE_ROW_MAX_ACTIONS) {
173
+ return (_jsx(ActionRow, { children: _jsx(ActionButtons, { actions: pageActions, dispatchAction: dispatchAction }) }));
174
+ }
175
+ const secondaries = pageActions.slice(0, -1);
176
+ const primary = pageActions[pageActions.length - 1];
177
+ return (_jsxs(Div, { className: "flex w-full flex-col gap-[var(--appkit-space-2)]", children: [_jsx(ActionRow, { children: _jsx(ActionButtons, { actions: secondaries, dispatchAction: dispatchAction }) }), _jsx(ActionRow, { children: _jsx(ActionButtons, { actions: [primary], dispatchAction: dispatchAction }) })] }));
178
+ }
106
179
  export default function BottomActions() {
107
180
  const { state, actionCallbacksRef, bulkCallbacksRef, bulkClearRef } = useBottomActionsContext();
108
181
  const [pickerOpen, setPickerOpen] = useState(false);
@@ -204,7 +277,7 @@ export default function BottomActions() {
204
277
  isVisible ? "" : "pointer-events-none",
205
278
  ]
206
279
  .filter(Boolean)
207
- .join(" "), children: [isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-surface active:bg-primary-200 dark:bg-primary-950/30 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
280
+ .join(" "), children: [isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b border-[var(--appkit-color-border-subtle)]", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b border-[var(--appkit-color-border-subtle)]", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `min-${BOTTOM_NAV_HEIGHT} py-[var(--appkit-space-2)]`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-surface active:bg-primary-200 dark:bg-primary-950/30 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
208
281
  ? `${bulk.selectedCount} ${bulk.noun}`
209
282
  : `${bulk.selectedCount} selected` })] }), bulkActions.length > 0 && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setPickerOpen((o) => !o), "aria-haspopup": "listbox", "aria-expanded": pickerOpen, className: [
210
283
  "flex-1 min-w-0 h-10 flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-3)] rounded-lg border text-[length:var(--appkit-text-sm)] font-medium transition-colors",
@@ -14,7 +14,7 @@
14
14
  * ```tsx
15
15
  * useBottomActions({
16
16
  * actions: [
17
- * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", grow: false, onClick: handleWishlist },
17
+ * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", onClick: handleWishlist },
18
18
  * { id: "cart", label: t("addToCart"), variant: "outline", onClick: handleAddToCart },
19
19
  * { id: "buy", label: t("buyNow"), variant: "primary", onClick: handleBuyNow },
20
20
  * ],
@@ -53,11 +53,6 @@ export interface BottomAction {
53
53
  disabled?: boolean;
54
54
  /** Show spinner inside the button. */
55
55
  loading?: boolean;
56
- /**
57
- * When true (default) the button grows to fill remaining space.
58
- * Set to false for compact icon-only buttons.
59
- */
60
- grow?: boolean;
61
56
  }
62
57
  export interface BottomBulkConfig {
63
58
  /** Number of currently selected items. Bar activates when > 0. */
@@ -16,7 +16,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
16
16
  * ```tsx
17
17
  * useBottomActions({
18
18
  * actions: [
19
- * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", grow: false, onClick: handleWishlist },
19
+ * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", onClick: handleWishlist },
20
20
  * { id: "cart", label: t("addToCart"), variant: "outline", onClick: handleAddToCart },
21
21
  * { id: "buy", label: t("buyNow"), variant: "primary", onClick: handleBuyNow },
22
22
  * ],
@@ -28,10 +28,10 @@ export function useBottomActions(options = {}) {
28
28
  const bulkRef = useRef(options.bulk);
29
29
  bulkRef.current = options.bulk;
30
30
  const actionKey = (options.actions ?? [])
31
- .map(({ id, label, variant, badge, disabled, loading, grow }) => `${id}|${label}|${variant}|${badge}|${disabled}|${loading}|${grow}`)
31
+ .map(({ id, label, variant, badge, disabled, loading }) => `${id}|${label}|${variant}|${badge}|${disabled}|${loading}`)
32
32
  .join(",");
33
33
  const bulkCountKey = options.bulk
34
- ? `${options.bulk.selectedCount}|${options.bulk.actions.map((a) => `${a.id}|${a.label}|${a.variant}|${a.badge}|${a.disabled}|${a.loading}|${a.grow}`).join(",")}`
34
+ ? `${options.bulk.selectedCount}|${options.bulk.actions.map((a) => `${a.id}|${a.label}|${a.variant}|${a.badge}|${a.disabled}|${a.loading}`).join(",")}`
35
35
  : "";
36
36
  useEffect(() => {
37
37
  if (!isTop)
@@ -1,4 +1,5 @@
1
1
  import { type FormShellContextValue } from "../../../ui/forms/FormShell";
2
+ import type { BottomAction } from "../BottomActionsContext";
2
3
  export interface UseFormBottomActionsOptions {
3
4
  /** Runs when the pinned primary button is pressed. */
4
5
  onSubmit: () => void | Promise<void>;
@@ -26,6 +27,16 @@ export interface UseFormBottomActionsOptions {
26
27
  onClick: () => void;
27
28
  disabled?: boolean;
28
29
  };
30
+ /**
31
+ * Further actions this form offers, spliced between the destructive one and
32
+ * Submit so the ordering rule survives: destructive furthest from the thumb,
33
+ * submit under it.
34
+ *
35
+ * Exists because the bar was hard-capped at three. `/admin/users` needs five
36
+ * (three forms plus soft-ban and hard-ban), so its extra actions had nowhere
37
+ * to go and stayed as an inline row the pinned bar then competed with.
38
+ */
39
+ extraActions?: BottomAction[];
29
40
  ctx?: FormShellContextValue;
30
41
  }
31
42
  /**
@@ -63,5 +74,5 @@ export interface UseFormBottomActionsOptions {
63
74
  * overlay tells us, because neither portal leaves a detectable trace in
64
75
  * the DOM at the form's position.
65
76
  */
66
- export declare function useFormBottomActions({ onSubmit, onCancel, submitLabel, cancelLabel, isLoading, disabled, enabled, destructiveAction, ctx: ctxProp, }: UseFormBottomActionsOptions): void;
77
+ export declare function useFormBottomActions({ onSubmit, onCancel, submitLabel, cancelLabel, isLoading, disabled, enabled, destructiveAction, extraActions, ctx: ctxProp, }: UseFormBottomActionsOptions): void;
67
78
  export default useFormBottomActions;
@@ -40,7 +40,7 @@ import { useBottomActions } from "./useBottomActions";
40
40
  * overlay tells us, because neither portal leaves a detectable trace in
41
41
  * the DOM at the form's position.
42
42
  */
43
- export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save", cancelLabel = "Cancel", isLoading = false, disabled = false, enabled = true, destructiveAction, ctx: ctxProp, }) {
43
+ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save", cancelLabel = "Cancel", isLoading = false, disabled = false, enabled = true, destructiveAction, extraActions, ctx: ctxProp, }) {
44
44
  const ambient = useContext(FormShellContext);
45
45
  const ctx = ctxProp ?? ambient;
46
46
  const insideOverlay = useIsInsideOverlay();
@@ -53,14 +53,18 @@ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save",
53
53
  const list = [];
54
54
  /*
55
55
  * Destructive FIRST, so it is furthest from the thumb's resting position
56
- * on the primary action. It never grows, so Save keeps the width.
56
+ * on the primary action, which BottomActions renders last.
57
+ *
58
+ * None of these set a width. `grow` no longer exists on BottomAction —
59
+ * ActionRow sizes every action from its own label and shares only the
60
+ * leftover, so "Cancel" and "Save" each get a width that fits their text
61
+ * instead of Cancel collapsing to a 44px square while Save took the rest.
57
62
  */
58
63
  if (destructiveAction) {
59
64
  list.push({
60
65
  id: "form-destructive",
61
66
  label: destructiveAction.label,
62
67
  variant: "danger",
63
- grow: false,
64
68
  onClick: destructiveAction.onClick,
65
69
  disabled: destructiveAction.disabled || isLoading,
66
70
  });
@@ -70,22 +74,24 @@ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save",
70
74
  id: "form-cancel",
71
75
  label: cancelLabel,
72
76
  variant: "outline",
73
- grow: false,
74
77
  onClick: onCancel,
75
78
  disabled: isLoading,
76
79
  });
77
80
  }
81
+ if (extraActions?.length)
82
+ list.push(...extraActions);
83
+ // Submit LAST: BottomActions treats the final action as the primary and
84
+ // gives it its own full-width line once the bar holds three or more.
78
85
  list.push({
79
86
  id: "form-submit",
80
87
  label: submitLabel,
81
88
  variant: "primary",
82
- grow: true,
83
89
  loading: isLoading,
84
90
  disabled: disabled || isLoading,
85
91
  onClick: () => void onSubmit(),
86
92
  });
87
93
  return list;
88
- }, [active, onCancel, cancelLabel, submitLabel, isLoading, disabled, onSubmit, destructiveAction]);
94
+ }, [active, onCancel, cancelLabel, submitLabel, isLoading, disabled, onSubmit, destructiveAction, extraActions]);
89
95
  useBottomActions({
90
96
  actions,
91
97
  // Encodes the count on purpose — see (1) in the header.
@@ -33,7 +33,10 @@ function toDescriptionHtml(raw) {
33
33
  export async function LiveItemDetailPageView({ slug, initialProduct, renderActions }) {
34
34
  const product = initialProduct !== undefined
35
35
  ? (initialProduct ?? undefined)
36
- : await getLiveItemForDetail(slug).catch(() => undefined);
36
+ // The listing IS this page's subject — a read failure must surface rather
37
+ // than become the same `undefined` a removed listing produces, which the
38
+ // "Not Found" branch below already handles.
39
+ : await getLiveItemForDetail(slug);
37
40
  if (!product) {
38
41
  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: "Live Listing Not Found" }), _jsx(Text, { color: "muted", children: "The live listing you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.LIVE), className: "text-[length:var(--appkit-text-sm)] font-medium text-primary-600 hover:underline", children: "Browse Live Listings" })] }) }) }) }));
39
42
  }
@@ -20,7 +20,16 @@ export declare const lotterySlotWriteSchema: z.ZodObject<{
20
20
  slotNumber: number;
21
21
  image?: string | undefined;
22
22
  }>;
23
+ /**
24
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
25
+ * by schema instance and every zod wrapper returns a new one.
26
+ */
23
27
  export declare const lotteryConfigWriteSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
28
+ pricingMode: z.ZodEnum<["uniform", "variable"]>;
29
+ uniformPrice: z.ZodOptional<z.ZodNumber>;
30
+ drawWindowDurationMinutes: z.ZodNumber;
31
+ maxPullsPerTransaction: z.ZodNumber;
32
+ maxPullsPerUser: z.ZodNumber;
24
33
  slots: z.ZodArray<z.ZodObject<{
25
34
  slotNumber: z.ZodNumber;
26
35
  name: z.ZodString;
@@ -37,11 +46,6 @@ export declare const lotteryConfigWriteSchema: z.ZodEffects<z.ZodEffects<z.ZodOb
37
46
  slotNumber: number;
38
47
  image?: string | undefined;
39
48
  }>, "many">;
40
- pricingMode: z.ZodEnum<["uniform", "variable"]>;
41
- uniformPrice: z.ZodOptional<z.ZodNumber>;
42
- drawWindowDurationMinutes: z.ZodNumber;
43
- maxPullsPerTransaction: z.ZodNumber;
44
- maxPullsPerUser: z.ZodNumber;
45
49
  }, "strict", z.ZodTypeAny, {
46
50
  slots: {
47
51
  name: string;
@@ -23,6 +23,17 @@
23
23
  * `weight` is absent for the same reason: it is derived from price
24
24
  * server-side and is never client input.
25
25
  *
26
+ * ## One schema, not two
27
+ *
28
+ * There was a second one — `lotteryConfigFormSchema`, in
29
+ * `_internal/shared/features/lottery/config-form.ts`. `LotteryAdminEditView`
30
+ * passed THAT to `<Form>` and parsed THIS in its submit handler, so the form
31
+ * schema's rules (the duplicate-slot check, the free-lottery check) had never
32
+ * once executed. It was deleted 2026-08-30 and its annotations moved here:
33
+ * `annotate()` keys a WeakMap by schema instance and the server ignores the
34
+ * registry entirely, so carrying layout metadata on the write contract costs
35
+ * the route nothing and removes the opportunity for the two to disagree.
36
+ *
26
37
  * ## Merging is BY `slotNumber`, not by index
27
38
  *
28
39
  * Array position is not identity. An admin who deletes slot 3 shifts every
@@ -46,6 +57,7 @@
46
57
  * @tag sideEffects:none
47
58
  */
48
59
  import { z } from "zod";
60
+ import { annotate } from "../../shell/field-ui-meta";
49
61
  /** Mirrors `LotteryPricingMode`. */
50
62
  const pricingModeSchema = z.enum(["uniform", "variable"]);
51
63
  /**
@@ -60,22 +72,70 @@ export const lotterySlotWriteSchema = z
60
72
  price: z.coerce.number().min(0, "A price cannot be negative."),
61
73
  })
62
74
  .strict();
75
+ /**
76
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
77
+ * by schema instance and every zod wrapper returns a new one.
78
+ */
63
79
  export const lotteryConfigWriteSchema = z
64
80
  .object({
65
- slots: z
81
+ pricingMode: annotate(pricingModeSchema, {
82
+ section: "pricing", sectionLabel: "Pricing", sectionRequired: true,
83
+ quick: true, order: 1, row: "pair", label: "Pricing mode",
84
+ }),
85
+ /*
86
+ * Hidden under variable pricing, and therefore not sent — `visibleValues`
87
+ * drops it, and the refine below only demands it in uniform mode. The two
88
+ * halves have to agree or a hidden field fails validation invisibly.
89
+ */
90
+ uniformPrice: annotate(z.coerce.number().min(0).optional(), {
91
+ section: "pricing", order: 2, row: "pair", kind: "number",
92
+ label: "Price per slot (₹)",
93
+ when: (v) => v.pricingMode === "uniform",
94
+ }),
95
+ drawWindowDurationMinutes: annotate(z.coerce.number().int().min(1).max(1440), {
96
+ section: "limits", sectionLabel: "Draw rules", order: 1, row: "third",
97
+ kind: "number", label: "Draw window (minutes)",
98
+ help: "How long a buyer has to complete a pull once it starts.",
99
+ }),
100
+ maxPullsPerTransaction: annotate(z.coerce.number().int().min(1).max(200), {
101
+ section: "limits", order: 2, row: "third", kind: "number",
102
+ label: "Max pulls per transaction",
103
+ }),
104
+ maxPullsPerUser: annotate(z.coerce.number().int().min(1).max(200), {
105
+ section: "limits", order: 3, row: "third", kind: "number",
106
+ label: "Max pulls per user",
107
+ }),
108
+ slots: annotate(z
66
109
  .array(lotterySlotWriteSchema)
67
110
  .min(1, "A lottery needs at least one slot.")
68
- .max(200, "A lottery cannot exceed 200 slots."),
69
- pricingMode: pricingModeSchema,
70
- uniformPrice: z.coerce.number().min(0).optional(),
71
- drawWindowDurationMinutes: z.coerce.number().int().min(1).max(1440),
72
- maxPullsPerTransaction: z.coerce.number().int().min(1).max(200),
73
- maxPullsPerUser: z.coerce.number().int().min(1).max(200),
111
+ .max(200, "A lottery cannot exceed 200 slots."), {
112
+ section: "slots", sectionLabel: "Slots", sectionRequired: true,
113
+ order: 1, row: "full", kind: "list", label: "Prize slots",
114
+ /*
115
+ * The slot table holds uncommitted rows — a name half-typed, an image
116
+ * URL pasted but not yet saved. `<Collapse>` unmounts its children, so
117
+ * without this a collapse discards them.
118
+ */
119
+ sectionKeepMounted: true,
120
+ }),
74
121
  })
75
122
  .strict()
76
123
  .refine((c) => new Set(c.slots.map((s) => s.slotNumber)).size === c.slots.length, { message: "Two slots share a slot number.", path: ["slots"] })
77
- .refine((c) => c.pricingMode !== "uniform" || typeof c.uniformPrice === "number", {
78
- message: "Uniform pricing needs a price.",
124
+ /*
125
+ * ABOVE zero, not merely present.
126
+ *
127
+ * This read `typeof c.uniformPrice === "number"`, so `0` passed — and the
128
+ * editor's payload builds the value as
129
+ * `Math.round(parseFloat(uniformPrice) * 100) / 100 || 0`, which turns
130
+ * "12,00" or "twelve" into exactly that. A typo became a FREE lottery,
131
+ * indistinguishable from deliberately pricing one at zero.
132
+ *
133
+ * `lotteryConfigFormSchema` has carried the correct `> 0` rule all along and
134
+ * has never executed: the editor passes it to `<Form>` and parses THIS schema
135
+ * instead. The rule belongs on the schema that runs.
136
+ */
137
+ .refine((c) => c.pricingMode !== "uniform" || (typeof c.uniformPrice === "number" && c.uniformPrice > 0), {
138
+ message: "Set a slot price above zero, or switch to variable pricing.",
79
139
  path: ["uniformPrice"],
80
140
  });
81
141
  /**
@@ -44,10 +44,12 @@ export async function finalizeStagedMediaUrl(url) {
44
44
  const finalPath = await moveToFinalPath(asset.storagePath);
45
45
  await mediaAssetsRepository.promoteToFinalized(shortId, finalPath);
46
46
  }
47
+ // Non-fatal by construction: the returned URL is the same before and after
48
+ // the move, so a failed promotion leaves the asset in tmp/ and the proxy
49
+ // keeps serving it from there until the tmp-cleanup job reclaims it.
47
50
  }
48
51
  catch (_err) {
49
52
  void normalizeError(_err);
50
- // Non-fatal: URL is still valid; proxy will serve whatever storagePath points to.
51
53
  }
52
54
  return url; // URL is stable — same before and after the file move
53
55
  }
@@ -63,7 +63,7 @@ export function ImageEditor({ isOpen, imageUrl, onClose, onSave, aspectRatio, ou
63
63
  }
64
64
  catch (err) {
65
65
  void normalizeError(err);
66
- showToast(err instanceof Error ? err.message : "Failed to save image.", "error");
66
+ showToast("Failed to save image.", "error");
67
67
  }
68
68
  finally {
69
69
  setSaving(false);
@@ -10,6 +10,7 @@ import { useState, useRef } from "react";
10
10
  import { useTranslations } from "next-intl";
11
11
  import { Alert, Button, Div, Modal, Row, Span, Spinner, Stack, Text } from "../../../ui";
12
12
  import { normalizeError } from "../../../errors/normalize";
13
+ import { toUserMessage } from "../../../errors/error-display-map";
13
14
  const __O = {
14
15
  hidden: "overflow-hidden",
15
16
  };
@@ -45,8 +46,10 @@ export function VideoThumbnailSelector({ isOpen, videoUrl, onClose, onSelect, on
45
46
  onClose();
46
47
  }
47
48
  catch (err) {
48
- void normalizeError(err);
49
- setError(err instanceof Error ? err.message : t("thumbnailError"));
49
+ const e = normalizeError(err);
50
+ // `t` is namespaced ("mediaEditor"), so it cannot resolve the absolute
51
+ // `errors.codes.*` keys — omit it and let the authored copy stand.
52
+ setError(toUserMessage(e.code, undefined, { fallback: t("thumbnailError") }));
50
53
  }
51
54
  finally {
52
55
  setIsCapturing(false);
@@ -11,6 +11,7 @@ import { Alert, Button, Div, Modal, Row, Span, Stack, Text } from "../../../ui";
11
11
  import { MediaSlider } from "../components/MediaSlider";
12
12
  import { useMediaTrim } from "../hooks/useMedia";
13
13
  import { normalizeError } from "../../../errors/normalize";
14
+ import { toUserMessage } from "../../../errors/error-display-map";
14
15
  function formatTime(seconds) {
15
16
  const m = Math.floor(seconds / 60);
16
17
  const s = Math.floor(seconds % 60);
@@ -62,8 +63,10 @@ export function VideoTrimModal({ isOpen, videoUrl, onClose, onSave, endpoint, })
62
63
  onClose();
63
64
  }
64
65
  catch (err) {
65
- void normalizeError(err);
66
- setError(err instanceof Error ? err.message : t("trimError"));
66
+ const e = normalizeError(err);
67
+ // `t` is namespaced ("mediaEditor"), so it cannot resolve the absolute
68
+ // `errors.codes.*` keys — omit it and let the authored copy stand.
69
+ setError(toUserMessage(e.code, undefined, { fallback: t("trimError") }));
67
70
  }
68
71
  };
69
72
  const qualityLabels = {
@@ -320,7 +320,7 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
320
320
  }
321
321
  catch (err) {
322
322
  void normalizeError(err);
323
- setError(err instanceof Error ? err.message : "Upload failed");
323
+ setError("Upload failed");
324
324
  }
325
325
  finally {
326
326
  setIsLoading(false);
@@ -362,7 +362,7 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
362
362
  }
363
363
  catch (err) {
364
364
  void normalizeError(err);
365
- setError(err instanceof Error ? err.message : "Upload failed");
365
+ setError("Upload failed");
366
366
  }
367
367
  finally {
368
368
  setIsLoading(false);
@@ -10,8 +10,18 @@
10
10
  * canonical Firestore message ordering wins.
11
11
  */
12
12
  import { useCallback, useEffect, useState } from "react";
13
- import { getClientRealtimeProvider } from "../../../contracts/client-realtime";
14
13
  import { acquireRealtimeSession } from "../../../contracts/realtime-session";
14
+ // One scope for both message hooks, deliberately: /api/realtime/token issues a
15
+ // SINGLE token covering all of this user's conversationIds, so a per-id scope
16
+ // would make the list and the detail hook re-sign against each other on the
17
+ // same page for no gain.
18
+ //
19
+ // 🛑 Known limitation, not fixed here: because the scope is constant, a
20
+ // conversation created AFTER sign-in is not in the cached token's claims and
21
+ // its subscription is denied until the token's expiry skew forces a re-sign.
22
+ // The denial is now surfaced (see the error callback below) rather than
23
+ // swallowed. The durable fix is to re-acquire on a permission denial.
24
+ const MESSAGES_SCOPE = "messages";
15
25
  import { conversationPingPath, userConversationsPingPath, } from "../realtime";
16
26
  import { normalizeError } from "../../../errors/normalize";
17
27
  import { apiClient, ApiClientError } from "../../../http/ApiClient";
@@ -83,12 +93,16 @@ export function useConversation(conversationId) {
83
93
  // because `send()` calls `refetch()` directly.
84
94
  void (async () => {
85
95
  try {
86
- release = await acquireRealtimeSession("messages", () => apiClient.post(ADMIN_ENDPOINTS.REALTIME_TOKEN, {}));
96
+ const lease = await acquireRealtimeSession(MESSAGES_SCOPE, () => apiClient.post(ADMIN_ENDPOINTS.REALTIME_TOKEN, {}));
97
+ release = lease.release;
87
98
  if (cancelled) {
88
- release?.();
99
+ release();
89
100
  return;
90
101
  }
91
- unsubscribe = getClientRealtimeProvider().subscribe(conversationPingPath(conversationId), () => {
102
+ // Subscribe through the LEASE's provider — it is bound to the app this
103
+ // scope authenticated on. The global provider may be on a different app
104
+ // carrying another channel's claims.
105
+ unsubscribe = lease.provider.subscribe(conversationPingPath(conversationId), () => {
92
106
  setIsConnected(true);
93
107
  void refetch();
94
108
  }, (err) => {
@@ -5,8 +5,18 @@
5
5
  * conversations gets a new message.
6
6
  */
7
7
  import { useCallback, useEffect, useState } from "react";
8
- import { getClientRealtimeProvider } from "../../../contracts/client-realtime";
9
8
  import { acquireRealtimeSession } from "../../../contracts/realtime-session";
9
+ // One scope for both message hooks, deliberately: /api/realtime/token issues a
10
+ // SINGLE token covering all of this user's conversationIds, so a per-id scope
11
+ // would make the list and the detail hook re-sign against each other on the
12
+ // same page for no gain.
13
+ //
14
+ // 🛑 Known limitation, not fixed here: because the scope is constant, a
15
+ // conversation created AFTER sign-in is not in the cached token's claims and
16
+ // its subscription is denied until the token's expiry skew forces a re-sign.
17
+ // The denial is now surfaced (see the error callback below) rather than
18
+ // swallowed. The durable fix is to re-acquire on a permission denial.
19
+ const MESSAGES_SCOPE = "messages";
10
20
  import { userConversationsPingPath } from "../realtime";
11
21
  import { normalizeError } from "../../../errors/normalize";
12
22
  import { apiClient } from "../../../http/ApiClient";
@@ -57,23 +67,30 @@ export function useConversations(userId, opts = {}) {
57
67
  // `// ignore`, so the whole live-list channel was inert while looking wired.
58
68
  void (async () => {
59
69
  try {
60
- release = await acquireRealtimeSession("messages", () => apiClient.post(ADMIN_ENDPOINTS.REALTIME_TOKEN, {}));
70
+ const lease = await acquireRealtimeSession(MESSAGES_SCOPE, () => apiClient.post(ADMIN_ENDPOINTS.REALTIME_TOKEN, {}));
71
+ release = lease.release;
61
72
  if (cancelled) {
62
- release?.();
73
+ release();
63
74
  return;
64
75
  }
65
- unsubscribe = getClientRealtimeProvider().subscribe(userConversationsPingPath(userId), () => {
76
+ // Subscribe through the LEASE's provider — see useConversation.
77
+ unsubscribe = lease.provider.subscribe(userConversationsPingPath(userId), () => {
66
78
  void refetch();
67
79
  }, (err) => {
68
- // Surfaced, not ignored. A denied subscription is a real defect and
69
- // silence here is what hid it for months.
80
+ // A denied subscription is a real defect — silence is what hid this
81
+ // channel being dead for months — so it is logged, not swallowed.
82
+ // (This comment previously claimed "Surfaced, not ignored" while the
83
+ // body only called normalizeError, which classifies without
84
+ // reporting. That was the same documentation-that-lies pattern this
85
+ // workstream exists to remove, so it now actually logs.)
70
86
  void normalizeError(err);
87
+ console.warn("[messages] conversations live-update subscription denied; list falls back to fetch-on-load");
71
88
  });
89
+ // Provider unregistered or token fetch failed — non-fatal; the list
90
+ // still works via the one-shot fetch above, just without live updates.
72
91
  }
73
92
  catch (_err) {
74
93
  void normalizeError(_err);
75
- // Provider unregistered or token fetch failed — non-fatal; the list
76
- // still works via the one-shot fetch above, just without live updates.
77
94
  }
78
95
  })();
79
96
  return () => {
@@ -16,6 +16,7 @@ import { Button, Checkbox, Div, Span, Stack, Text, Textarea } from "../../../ui"
16
16
  import { REFUND_COPY } from "../../../_internal/shared/features/orders/refund-copy";
17
17
  import { formatCurrency } from "../../../utils/number.formatter";
18
18
  import { normalizeError } from "../../../errors/normalize";
19
+ import { toUserMessage } from "../../../errors/error-display-map";
19
20
  const ACK_COUNT = REFUND_COPY.request.acknowledgments.length;
20
21
  const CLS_WARN_PANEL = "rounded-xl border border-warning bg-warning-surface p-[var(--appkit-space-4)] dark:border-warning";
21
22
  export function RefundRequestView({ order, onSubmitRequest, isLoading = false, className = "", }) {
@@ -37,8 +38,10 @@ export function RefundRequestView({ order, onSubmitRequest, isLoading = false, c
37
38
  await onSubmitRequest(reason.trim());
38
39
  }
39
40
  catch (err) {
40
- void normalizeError(err);
41
- setError(err instanceof Error ? err.message : REFUND_COPY.request.errorFallback);
41
+ const e = normalizeError(err);
42
+ setError(toUserMessage(e.code, undefined, {
43
+ fallback: REFUND_COPY.request.errorFallback,
44
+ }));
42
45
  }
43
46
  };
44
47
  return (_jsxs(Stack, { gap: "md", className: className, children: [_jsxs(Text, { size: "sm", color: "muted", children: [REFUND_COPY.request.orderTotalLabel, " ", _jsx(Span, { weight: "bold", children: formatCurrency(order.totalPrice, order.currency ?? "INR") })] }), _jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: [REFUND_COPY.request.reasonLabel, " ", _jsx(Span, { "aria-hidden": true, children: "*" })] }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value), placeholder: REFUND_COPY.request.reasonPlaceholder, rows: 3, "aria-required": "true", "aria-label": REFUND_COPY.request.reasonLabel })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", children: REFUND_COPY.request.acknowledgeHeading }), REFUND_COPY.request.acknowledgments.map((text, i) => (_jsx(Checkbox, { checked: acknowledged[i], onChange: () => handleToggle(i), label: _jsx(Text, { size: "sm", className: "leading-snug", children: text }), "aria-label": `Acknowledgment ${i + 1}` }, i)))] }), error && (_jsx(Text, { size: "sm", color: "danger", role: "alert", children: error })), _jsx(Button, { variant: "danger", onClick: handleSubmit, disabled: !canSubmit, "aria-busy": isLoading, children: isLoading ? REFUND_COPY.request.submittingLabel : REFUND_COPY.request.submitLabel })] }));