@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
@@ -0,0 +1,142 @@
1
+ import { z } from "zod";
2
+ /** The two member sources, as the rule-type select offers them. */
3
+ export declare const BUNDLE_RULE_TYPE_OPTIONS: {
4
+ value: string;
5
+ label: string;
6
+ }[];
7
+ /**
8
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
9
+ * by schema instance and every zod wrapper returns a new one.
10
+ */
11
+ export declare const bundleFormSchema: z.ZodEffects<z.ZodObject<{
12
+ name: z.ZodString;
13
+ priceRupees: z.ZodEffects<z.ZodString, string, string>;
14
+ description: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
15
+ ruleType: z.ZodEnum<["static", "dynamic"]>;
16
+ productIds: z.ZodArray<z.ZodString, "many">;
17
+ dynamicRule: z.ZodObject<{
18
+ type: z.ZodLiteral<"dynamic">;
19
+ filter: z.ZodObject<{
20
+ categorySlug: z.ZodOptional<z.ZodString>;
21
+ brandSlug: z.ZodOptional<z.ZodString>;
22
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
23
+ listingType: z.ZodOptional<z.ZodEnum<["standard", "pre-order", "prize-draw"]>>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
26
+ categorySlug?: string | undefined;
27
+ brandSlug?: string | undefined;
28
+ tags?: string[] | undefined;
29
+ }, {
30
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
31
+ categorySlug?: string | undefined;
32
+ brandSlug?: string | undefined;
33
+ tags?: string[] | undefined;
34
+ }>;
35
+ orderBy: z.ZodOptional<z.ZodEnum<["price-asc", "price-desc", "createdAt-desc"]>>;
36
+ limit: z.ZodNumber;
37
+ }, "strip", z.ZodTypeAny, {
38
+ type: "dynamic";
39
+ filter: {
40
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
41
+ categorySlug?: string | undefined;
42
+ brandSlug?: string | undefined;
43
+ tags?: string[] | undefined;
44
+ };
45
+ limit: number;
46
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
47
+ }, {
48
+ type: "dynamic";
49
+ filter: {
50
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
51
+ categorySlug?: string | undefined;
52
+ brandSlug?: string | undefined;
53
+ tags?: string[] | undefined;
54
+ };
55
+ limit: number;
56
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
57
+ }>;
58
+ coverImage: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
59
+ brandSlug: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
60
+ isActive: z.ZodBoolean;
61
+ }, "strip", z.ZodTypeAny, {
62
+ name: string;
63
+ isActive: boolean;
64
+ productIds: string[];
65
+ priceRupees: string;
66
+ ruleType: "static" | "dynamic";
67
+ dynamicRule: {
68
+ type: "dynamic";
69
+ filter: {
70
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
71
+ categorySlug?: string | undefined;
72
+ brandSlug?: string | undefined;
73
+ tags?: string[] | undefined;
74
+ };
75
+ limit: number;
76
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
77
+ };
78
+ brandSlug?: string | undefined;
79
+ description?: string | undefined;
80
+ coverImage?: string | undefined;
81
+ }, {
82
+ name: string;
83
+ isActive: boolean;
84
+ productIds: string[];
85
+ priceRupees: string;
86
+ ruleType: "static" | "dynamic";
87
+ dynamicRule: {
88
+ type: "dynamic";
89
+ filter: {
90
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
91
+ categorySlug?: string | undefined;
92
+ brandSlug?: string | undefined;
93
+ tags?: string[] | undefined;
94
+ };
95
+ limit: number;
96
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
97
+ };
98
+ brandSlug?: string | undefined;
99
+ description?: string | undefined;
100
+ coverImage?: string | undefined;
101
+ }>, {
102
+ name: string;
103
+ isActive: boolean;
104
+ productIds: string[];
105
+ priceRupees: string;
106
+ ruleType: "static" | "dynamic";
107
+ dynamicRule: {
108
+ type: "dynamic";
109
+ filter: {
110
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
111
+ categorySlug?: string | undefined;
112
+ brandSlug?: string | undefined;
113
+ tags?: string[] | undefined;
114
+ };
115
+ limit: number;
116
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
117
+ };
118
+ brandSlug?: string | undefined;
119
+ description?: string | undefined;
120
+ coverImage?: string | undefined;
121
+ }, {
122
+ name: string;
123
+ isActive: boolean;
124
+ productIds: string[];
125
+ priceRupees: string;
126
+ ruleType: "static" | "dynamic";
127
+ dynamicRule: {
128
+ type: "dynamic";
129
+ filter: {
130
+ listingType?: "standard" | "pre-order" | "prize-draw" | undefined;
131
+ categorySlug?: string | undefined;
132
+ brandSlug?: string | undefined;
133
+ tags?: string[] | undefined;
134
+ };
135
+ limit: number;
136
+ orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
137
+ };
138
+ brandSlug?: string | undefined;
139
+ description?: string | undefined;
140
+ coverImage?: string | undefined;
141
+ }>;
142
+ export type BundleFormValues = z.infer<typeof bundleFormSchema>;
@@ -0,0 +1,149 @@
1
+ /*
2
+ * WHY: `AdminBundleEditorView` declared a seven-field `.passthrough()` schema
3
+ * inside the component, passed it to `<Form schema>`, and never parsed it
4
+ * — the submit handler ran two hand-written `if` checks instead. So the
5
+ * only rules that ever ran were "name is non-empty" and "price parses",
6
+ * and the member-count bounds the form DISPLAYS (`min 3, max 16`) were
7
+ * rendered as red hint text under the picker with nothing stopping a save.
8
+ * A bundle with one member, or with twenty, saved cleanly.
9
+ * WHAT: The bundle editor's schema — the real field set, annotated for
10
+ * `buildSectionsFromSchema`, with the member-count bounds as a
11
+ * `superRefine` keyed on the rule type.
12
+ *
13
+ * ## `dynamicRule` is a field, not a side-car
14
+ *
15
+ * The editor held it in component state beside the schema, so `visibleValues`
16
+ * could not see it and the payload builder had to restate the static/dynamic
17
+ * branch by hand. Declaring it here is what makes "a hidden control is not
18
+ * sent" structural: `ruleType` switches which of the two member sources is on
19
+ * screen, and the same predicate decides which reaches the payload.
20
+ *
21
+ * ## Prices stay strings
22
+ *
23
+ * `priceRupees` comes from an `<input>`, so it is a string here and coerced
24
+ * once, in the payload builder. Coercing inside the schema would put a
25
+ * `Number()` at every call site, which is how a `NaN` reaches Firestore.
26
+ *
27
+ * EXPORTS:
28
+ * bundleFormSchema, type BundleFormValues, BUNDLE_RULE_TYPE_OPTIONS
29
+ *
30
+ * @tag domain:categories
31
+ * @tag layer:schema
32
+ * @tag pattern:none
33
+ * @tag access:isomorphic
34
+ * @tag consumers:AdminBundleEditorView
35
+ * @tag sideEffects:none
36
+ */
37
+ import { z } from "zod";
38
+ import { annotate } from "../../shell/field-ui-meta";
39
+ import { BUNDLE_COPY } from "../../../_internal/shared/features/categories/bundle-copy";
40
+ import { BUNDLE_MIN_ITEMS, BUNDLE_MAX_ITEMS, } from "../../../_internal/shared/features/categories/bundle-config";
41
+ /** The two member sources, as the rule-type select offers them. */
42
+ export const BUNDLE_RULE_TYPE_OPTIONS = [
43
+ { value: "static", label: BUNDLE_COPY.adminEditor.ruleTypeStatic },
44
+ { value: "dynamic", label: BUNDLE_COPY.adminEditor.ruleTypeDynamic },
45
+ ];
46
+ /**
47
+ * The dynamic rule, as `BundleDynamicRuleEditor` edits it.
48
+ *
49
+ * Mirrors the `type: "dynamic"` arm of `BundleQueryRule` so the draft can be
50
+ * handed to the API without a cast.
51
+ */
52
+ const dynamicRuleSchema = z.object({
53
+ type: z.literal("dynamic"),
54
+ filter: z.object({
55
+ categorySlug: z.string().optional(),
56
+ brandSlug: z.string().optional(),
57
+ tags: z.array(z.string()).optional(),
58
+ listingType: z.enum(["standard", "pre-order", "prize-draw"]).optional(),
59
+ }),
60
+ orderBy: z.enum(["price-asc", "price-desc", "createdAt-desc"]).optional(),
61
+ limit: z.number().int().min(1).max(50),
62
+ });
63
+ /**
64
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
65
+ * by schema instance and every zod wrapper returns a new one.
66
+ */
67
+ export const bundleFormSchema = z
68
+ .object({
69
+ name: annotate(z
70
+ .string()
71
+ .min(1, BUNDLE_COPY.adminEditor.errors.nameRequired)
72
+ .max(150, "Keep the name under 150 characters."), {
73
+ section: "basics", sectionLabel: "Bundle", sectionRequired: true,
74
+ quick: true, order: 1, row: "pair", label: "Name",
75
+ }),
76
+ priceRupees: annotate(z
77
+ .string()
78
+ .regex(/^\d+(\.\d{1,2})?$/, BUNDLE_COPY.adminEditor.errors.priceInvalid)
79
+ .refine((v) => Number(v) > 0, BUNDLE_COPY.adminEditor.errors.priceInvalid), {
80
+ section: "basics", quick: true, order: 2, row: "pair",
81
+ kind: "number", label: "Bundle price (₹)",
82
+ help: "What the buyer pays for the whole bundle — normally less than the members' total.",
83
+ }),
84
+ description: annotate(z.string().max(2000).optional().or(z.literal("")), {
85
+ section: "basics", order: 3, row: "full", kind: "textarea",
86
+ label: BUNDLE_COPY.adminEditor.fields.descriptionLabel,
87
+ }),
88
+ ruleType: annotate(z.enum(["static", "dynamic"]), {
89
+ section: "members", sectionLabel: BUNDLE_COPY.picker.title,
90
+ order: 1, row: "full", kind: "select",
91
+ label: BUNDLE_COPY.adminEditor.ruleTypeLabel,
92
+ }),
93
+ /*
94
+ * Bounded in the `superRefine` rather than with `.min()/.max()` here,
95
+ * because an empty picker on a DYNAMIC bundle is correct — the resolver
96
+ * fills the mirror. A flat `.min(3)` would block every dynamic save.
97
+ */
98
+ productIds: annotate(z.array(z.string()), {
99
+ section: "members", order: 2, row: "full", kind: "list",
100
+ label: `${BUNDLE_COPY.adminEditor.ruleTypeStatic} (${BUNDLE_MIN_ITEMS}–${BUNDLE_MAX_ITEMS})`,
101
+ when: (v) => v.ruleType !== "dynamic",
102
+ }),
103
+ dynamicRule: annotate(dynamicRuleSchema, {
104
+ section: "members", order: 3, row: "full",
105
+ label: BUNDLE_COPY.adminEditor.dynamic.title,
106
+ help: BUNDLE_COPY.adminEditor.dynamic.hint,
107
+ when: (v) => v.ruleType === "dynamic",
108
+ }),
109
+ coverImage: annotate(z
110
+ .string()
111
+ .url("Enter a full image URL, starting with http.")
112
+ .optional()
113
+ .or(z.literal("")), {
114
+ section: "media", sectionLabel: "Presentation", order: 1, row: "full",
115
+ inputType: "url", label: BUNDLE_COPY.adminEditor.fields.coverImageLabel,
116
+ }),
117
+ brandSlug: annotate(z.string().optional().or(z.literal("")), {
118
+ section: "media", order: 2, row: "pair", kind: "select", label: "Brand",
119
+ help: "The bundle's own brand tag — independent of how its members resolve.",
120
+ }),
121
+ isActive: annotate(z.boolean(), {
122
+ section: "visibility", sectionLabel: "Visibility", order: 1, row: "full",
123
+ label: BUNDLE_COPY.adminEditor.fields.activeLabel,
124
+ }),
125
+ })
126
+ .superRefine((v, ctx) => {
127
+ /*
128
+ * The bounds the picker has always DISPLAYED, now enforced. They were two
129
+ * `<Text color="danger">` hints under the multi-select and nothing read
130
+ * them — a two-member bundle saved, and `listBundleMembers` rendered it as
131
+ * a "bundle" of two.
132
+ */
133
+ if (v.ruleType !== "static")
134
+ return;
135
+ if (v.productIds.length < BUNDLE_MIN_ITEMS) {
136
+ ctx.addIssue({
137
+ code: z.ZodIssueCode.custom,
138
+ path: ["productIds"],
139
+ message: `Select at least ${BUNDLE_MIN_ITEMS} products (currently ${v.productIds.length}).`,
140
+ });
141
+ }
142
+ else if (v.productIds.length > BUNDLE_MAX_ITEMS) {
143
+ ctx.addIssue({
144
+ code: z.ZodIssueCode.custom,
145
+ path: ["productIds"],
146
+ message: `Maximum ${BUNDLE_MAX_ITEMS} products allowed (currently ${v.productIds.length}).`,
147
+ });
148
+ }
149
+ });
@@ -9,6 +9,7 @@
9
9
  import { timingSafeEqual } from "crypto";
10
10
  import { ValidationError } from "../../../errors";
11
11
  import { normalizeError } from "../../../errors/normalize";
12
+ import { safeRead } from "../../../errors/safe-read";
12
13
  import { serverLogger } from "../../../monitoring";
13
14
  import { sendEmail } from "../../contact/email";
14
15
  import { getAdminDb } from "../../../providers/db-firebase";
@@ -47,7 +48,15 @@ export async function sendCheckoutValueOtp(userId, userEmail, channel = "email",
47
48
  if (!addressId) {
48
49
  throw new ValidationError("Select a delivery address before sending the code via WhatsApp.");
49
50
  }
50
- const settings = await siteSettingsRepository.getSingleton().catch(() => null);
51
+ // A failed settings read makes the WhatsApp channel look deliberately
52
+ // switched off ("send the code by email instead") — a defensible fallback,
53
+ // but one an operator has to be able to see, since it is indistinguishable
54
+ // from an admin having disabled it.
55
+ const settings = await safeRead(() => siteSettingsRepository.getSingleton(), {
56
+ route: "checkout/value-otp",
57
+ key: "checkoutValueOtp.siteSettings",
58
+ fallback: null,
59
+ });
51
60
  const creds = settings?.credentials ?? {};
52
61
  const whatsappOtpEnabled = settings?.notificationChannels?.whatsapp?.otpEnabled === true;
53
62
  const phoneNumberId = creds.whatsappPhoneNumberId?.trim() ?? "";
@@ -36,7 +36,7 @@ export function ClassifiedContactSellerPanel({ productId, onContactSeller }) {
36
36
  }
37
37
  catch (e) {
38
38
  void normalizeError(e);
39
- setError(e instanceof Error ? e.message : "Something went wrong");
39
+ setError("Something went wrong");
40
40
  }
41
41
  finally {
42
42
  setPending(false);
@@ -1,22 +1,23 @@
1
+ import { type OfferBounds } from "../../../_internal/shared/features/offers/config";
1
2
  import type { ProductDocument } from "../../products/schemas/firestore";
2
3
  export interface ClassifiedDetailPageViewProps {
3
4
  slug: string;
4
5
  /** Pre-fetched product document — dedupes with generateMetadata() via React.cache(). */
5
6
  initialProduct?: ProductDocument | null;
6
7
  /**
7
- * Render prop for offer UI, mounted inside the contact-seller panel.
8
+ * Render prop for the offer UI — which, on a classified, IS the purchase path.
8
9
  *
9
10
  * Deliberately the SAME shape `ProductDetailPageView.renderOfferAction` uses,
10
11
  * so both detail views share one contract and both consumer pages share one
11
- * snippet. Called only when all three offer gates pass: `featureFlags.offers`,
12
- * the classified type's `canMakeOffer` capability, and the seller's own
13
- * `allowOffers` opt-in.
12
+ * snippet. `bounds` comes from `resolveOfferBounds()`, the same function
13
+ * `makeOffer` enforces server-side.
14
14
  */
15
15
  renderOfferAction?: (opts: {
16
16
  productId: string;
17
17
  price: number;
18
18
  currency: string;
19
19
  minOfferPercent: number;
20
+ bounds: OfferBounds;
20
21
  }) => React.ReactNode;
21
22
  }
22
23
  export declare function ClassifiedDetailPageView({ slug, initialProduct, renderOfferAction }: ClassifiedDetailPageViewProps): Promise<import("react").JSX.Element>;
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
3
  import { getClassifiedForDetail } from "../../../_internal/server/features/classified/data";
4
- import { startClassifiedConversationAction } from "../../../_internal/server/features/classified/actions";
5
4
  const CLS_BREADCRUMB_LINK = "hover:text-primary-600 transition-colors";
6
5
  const CLS_CLASSIFIED_BADGE = "inline-block rounded-full bg-cyan-100 dark:bg-cyan-900/30 px-[var(--appkit-space-2-5)] py-[var(--appkit-space-0-5)] text-cyan-700 dark:text-cyan-300";
7
6
  import { ROUTES } from "../../../next";
@@ -20,10 +19,9 @@ import { RelatedItemsSection } from "../../products/components/RelatedItemsSecti
20
19
  import { computeRelatedItems } from "../../../_internal/server/features/products/data";
21
20
  import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
22
21
  import { getGroupsWithItemsForProduct } from "../../../_internal/server/features/grouped/data";
23
- import { ClassifiedContactSellerPanel } from "./ClassifiedContactSellerPanel";
24
22
  import { getSiteSettingsGlobal } from "../../admin/utils/getSiteSettingsGlobal";
25
- import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities";
26
- import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config";
23
+ import { safeRead } from "../../../errors/safe-read";
24
+ import { DEFAULT_MIN_OFFER_PERCENT, offersEnabledFor, resolveOfferBounds, } from "../../../_internal/shared/features/offers/config";
27
25
  import { ListingBottomActions } from "../../products/components/ListingBottomActions";
28
26
  function toDescriptionHtml(raw) {
29
27
  if (!raw)
@@ -34,7 +32,10 @@ function toDescriptionHtml(raw) {
34
32
  export async function ClassifiedDetailPageView({ slug, initialProduct, renderOfferAction }) {
35
33
  const product = initialProduct !== undefined
36
34
  ? (initialProduct ?? undefined)
37
- : await getClassifiedForDetail(slug).catch(() => undefined);
35
+ // The listing IS this page's subject — a read failure must surface rather
36
+ // than become the same `undefined` a removed listing produces, which the
37
+ // "Not Found" branch below already handles.
38
+ : await getClassifiedForDetail(slug);
38
39
  if (!product) {
39
40
  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: "Classified Listing Not Found" }), _jsx(Text, { color: "muted", children: "The classified listing you are looking for may have been removed." }), _jsx(Link, { href: String(ROUTES.PUBLIC.CLASSIFIED), className: "text-[length:var(--appkit-text-sm)] font-medium text-primary-600 hover:underline", children: "Browse Classifieds" })] }) }) }) }));
40
41
  }
@@ -58,33 +59,61 @@ export async function ClassifiedDetailPageView({ slug, initialProduct, renderOff
58
59
  const [{ relatedItems, relatedByBrand, relatedByTags, relatedByStore }, groups, siteSettings] = await Promise.all([
59
60
  computeRelatedItems(product),
60
61
  getGroupsWithItemsForProduct(product.id),
61
- getSiteSettingsGlobal().catch(() => null),
62
+ safeRead(() => getSiteSettingsGlobal(), {
63
+ route: "/classified/[slug]",
64
+ key: "classified.siteSettings",
65
+ fallback: null,
66
+ }),
62
67
  ]);
63
- // Classifieds are the one non-standard type where haggling is the norm, so
64
- // `canMakeOffer` is true for them — but the seller still opts in per listing
65
- // (`allowOffers`, usually alongside `classified.negotiable`).
66
- const offersAvailable = siteSettings?.featureFlags?.offers !== false &&
67
- canMakeOffer("classified") &&
68
- p.allowOffers === true &&
69
- price !== null &&
70
- !!renderOfferAction;
68
+ /*
69
+ * The offer IS the purchase path here — classified has `cartLine: "blocked"`,
70
+ * and until 2026-08-31 the only other route to the seller was a `conversations`
71
+ * chat thread, which no longer exists.
72
+ *
73
+ * So `offersEnabledFor` does NOT require `allowOffers` on a type with no cart:
74
+ * unchecking it would leave the page showing a price and no way to act on it.
75
+ * What it controls instead is the FLOOR — a seller who did not opt into
76
+ * haggling gets requests to buy at their asking price, never below it. Both
77
+ * facts come from `resolveOfferBounds`, which `makeOffer` re-derives.
78
+ */
79
+ // Narrowed, not cast: the document's fields are `FirestoreValue`, which
80
+ // includes `null`, and `allowOffers` decides a money rule.
81
+ const allowOffers = p.allowOffers === true;
82
+ const offersAvailable = offersEnabledFor("classified", allowOffers) && price !== null && !!renderOfferAction;
83
+ const offerBounds = price !== null
84
+ ? resolveOfferBounds({
85
+ listingType: "classified",
86
+ listedPrice: price,
87
+ minOfferPercent: typeof p.minOfferPercent === "number" ? p.minOfferPercent : undefined,
88
+ allowOffers,
89
+ })
90
+ : null;
71
91
  return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: String(p.id ?? p.slug ?? ""), productType: "product", snapshot: {
72
92
  title,
73
93
  thumb: images[0],
74
94
  price: price ?? undefined,
75
95
  storeId: typeof p.storeId === "string" ? p.storeId : undefined,
76
96
  storeName: storeName ?? undefined,
77
- } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CLASSIFIED), className: CLS_BREADCRUMB_LINK, children: "Classifieds" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_CLASSIFIED_BADGE, children: "Classified" }), meta?.negotiable && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Negotiable" })), meta?.acceptsShipping && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Shipping available" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: formatCurrency(price, currency) })), location && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCCD" }), _jsx(Span, { children: location }), meta?.meetupArea?.pincode && _jsxs(Span, { children: ["\u00B7 ", meta.meetupArea.pincode] })] })), meta?.contactMethod && (_jsxs(Text, { size: "xs", color: "muted", children: ["Contact via: ", meta.contactMethod === "both" ? "chat or phone" : meta.contactMethod] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: specs.map((s, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-36 flex-shrink-0", color: "primary", weight: "medium", children: s.name }), _jsxs(Dd, { className: "flex-1", color: "muted", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
97
+ } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CLASSIFIED), className: CLS_BREADCRUMB_LINK, children: "Classifieds" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => _jsx(ProductGalleryClient, { images: images, productName: title }), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_CLASSIFIED_BADGE, children: "Classified" }), meta?.negotiable && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Negotiable" })), meta?.acceptsShipping && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: "Shipping available" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), price !== null && (_jsx(Text, { size: "2xl", weight: "bold", color: "primary", children: formatCurrency(price, currency) })), location && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCCD" }), _jsx(Span, { children: location }), meta?.meetupArea?.pincode && _jsxs(Span, { children: ["\u00B7 ", meta.meetupArea.pincode] })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { border: "subtle", rounded: "xl", surface: "muted", padding: "md", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: specs.map((s, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-36 flex-shrink-0", color: "primary", weight: "medium", children: s.name }), _jsxs(Dd, { className: "flex-1", color: "muted", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
78
98
  id: s.id,
79
99
  label: s.title,
80
100
  content: _jsx(CustomSectionTabContent, { section: s }),
81
- })) })), renderBuyBar: () => (_jsxs(Stack, { id: "classified-contact-bar", className: "p-[var(--appkit-space-5)]", border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [offersAvailable &&
82
- renderOfferAction({
101
+ })) })), renderBuyBar: () => (_jsx(Stack, { id: "classified-contact-bar", className: "p-[var(--appkit-space-5)]", border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: offersAvailable && offerBounds ? (_jsxs(_Fragment, { children: [renderOfferAction({
83
102
  productId: String(product.id),
84
103
  price: price,
85
104
  currency,
86
105
  minOfferPercent: typeof p.minOfferPercent === "number"
87
106
  ? p.minOfferPercent
88
107
  : DEFAULT_MIN_OFFER_PERCENT,
89
- }), _jsx(ClassifiedContactSellerPanel, { productId: String(product.id), onContactSeller: startClassifiedConversationAction })] })), renderRelated: () => (_jsxs(Stack, { gap: "xl", children: [_jsx(GroupedListingsCarousel, { groups: groups }), _jsx(RelatedItemsSection, { relatedItems: relatedItems, relatedByBrand: relatedByBrand, relatedByTags: relatedByTags, relatedByStore: relatedByStore, categoryLabel: categoryName || category || undefined, brandLabel: brand, storeLabel: storeName ?? undefined })] })) }), _jsx(ListingBottomActions, { listingType: "classified", anchorId: "classified-contact-bar", price: price, currency: currency })] })] }));
108
+ bounds: offerBounds,
109
+ }), _jsx(Text, { size: "xs", color: "muted", children: offerBounds.isBuyRequest
110
+ ? "Send a request to buy and the seller will confirm. Payment and collection are arranged after they accept."
111
+ : "Offer what you think it's worth — the seller can accept, decline or counter." })] })) : (
112
+ /*
113
+ * Reachable only when the listing has no price at all: a
114
+ * classified always accepts offers otherwise (see the gate
115
+ * above). Saying so beats an empty bordered box, which is what
116
+ * this bar rendered while the contact panel was being removed.
117
+ */
118
+ _jsx(Text, { size: "sm", color: "muted", children: "This listing doesn't have a price yet, so it isn't accepting offers. Check back shortly." })) })), renderRelated: () => (_jsxs(Stack, { gap: "xl", children: [_jsx(GroupedListingsCarousel, { groups: groups }), _jsx(RelatedItemsSection, { relatedItems: relatedItems, relatedByBrand: relatedByBrand, relatedByTags: relatedByTags, relatedByStore: relatedByStore, categoryLabel: categoryName || category || undefined, brandLabel: brand, storeLabel: storeName ?? undefined })] })) }), _jsx(ListingBottomActions, { listingType: "classified", anchorId: "classified-contact-bar", price: price, currency: currency })] })] }));
90
119
  }
@@ -45,7 +45,7 @@ export function ConsultationForm({ onSubmit, isPending, concerns = [], }) {
45
45
  }
46
46
  return (_jsxs(Form, { schema: bookConsultationSchema, onSubmit: handleSubmit, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "name", type: "text", placeholder: "Full name", value: form.name, onChange: (v) => handleValueChange("name", v), required: true }), _jsx(FieldInput, { name: "email", type: "email", placeholder: "Email address", value: form.email, onChange: (v) => handleValueChange("email", v), required: true }), _jsx(FieldInput, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: (v) => handleValueChange("phone", v), required: true }), concerns.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: concerns.map((c) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => toggleConcern(c), className: `rounded-full border px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] transition ${form.concern.includes(c)
47
47
  ? "border-primary bg-primary text-white"
48
- : "border-neutral-300 text-neutral-700 hover:border-primary"}`, children: c }, c))) })), _jsxs(Row, { gap: "3", children: [_jsx(FieldInput, { name: "preferredDate", type: "date", value: form.preferredDate, onChange: (v) => handleValueChange("preferredDate", v), required: true, className: "flex-1" }), _jsx(Select, { value: form.preferredTime, onValueChange: (value) => handleValueChange("preferredTime", value), options: TIME_SLOTS.map((t) => ({ value: t, label: t })), wrapperClassName: "flex-1" })] }), _jsx(Select, { value: form.mode, onValueChange: (value) => handleValueChange("mode", value), options: [
48
+ : "border-neutral-300 text-neutral-700 hover:border-primary"}`, children: c }, c))) })), _jsxs(Row, { gap: "3", children: [_jsx(FieldInput, { name: "preferredDate", type: "date", value: form.preferredDate, onChange: (v) => handleValueChange("preferredDate", v), required: true, className: "flex-1" }), _jsx(Select, { name: "preferredTime", label: "Preferred time", value: form.preferredTime, onValueChange: (value) => handleValueChange("preferredTime", value), options: TIME_SLOTS.map((t) => ({ value: t, label: t })), wrapperClassName: "flex-1" })] }), _jsx(Select, { name: "mode", label: "How would you like to meet?", value: form.mode, onValueChange: (value) => handleValueChange("mode", value), options: [
49
49
  { value: "remote", label: "Remote (Video Call)" },
50
50
  { value: "in-person", label: "In-Person" },
51
51
  ] }), _jsx(FieldTextarea, { name: "message", placeholder: "Any additional notes (optional)", value: form.message, onChange: (v) => handleValueChange("message", v), rows: 3 }), _jsx(Button, { type: "submit", isLoading: isPending, disabled: isPending, variant: "primary", className: "w-full", children: isPending ? "Booking..." : "Book Consultation" })] }));
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { annotate } from "../../shell/field-ui-meta";
2
3
  export const consultationStatusSchema = z.enum([
3
4
  "pending",
4
5
  "confirmed",
@@ -36,29 +37,52 @@ export const consultationBookingSchema = z.object({
36
37
  * Form schema for submitting a new consultation booking.
37
38
  */
38
39
  export const bookConsultationSchema = z.object({
39
- name: z.string().trim().min(2, "Please tell us your name.").max(120),
40
- email: z
40
+ name: annotate(z.string().trim().min(2, "Please tell us your name.").max(120), {
41
+ section: "who", sectionLabel: "Your details", sectionRequired: true,
42
+ order: 1, row: "pair", label: "Your name",
43
+ }),
44
+ email: annotate(z
41
45
  .string()
42
46
  .trim()
43
47
  .toLowerCase()
44
48
  .min(1, "An email address is required.")
45
- .email("That doesn't look like an email address."),
49
+ .email("That doesn't look like an email address."), { section: "who", order: 2, row: "pair", inputType: "email", label: "Email" }),
46
50
  // Same shape as the corporate enquiry's — see the note there.
47
- phone: z
51
+ phone: annotate(z
48
52
  .string()
49
53
  .trim()
50
54
  .regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
51
55
  .optional()
52
- .or(z.literal("")),
56
+ .or(z.literal("")), { section: "who", order: 3, row: "pair", inputType: "tel", label: "Phone" }),
53
57
  /*
54
58
  * Optional here, though the document treats it as the subject of the
55
59
  * booking: the public form does not render a concern picker, so requiring it
56
60
  * would reject every submission the form can produce. The admin-side booking
57
61
  * path is where it is set.
58
62
  */
59
- concern: z.array(z.string()).optional(),
60
- preferredDate: z.string().optional(),
61
- preferredTime: z.string().optional(),
62
- mode: consultationModeSchema.optional(),
63
- message: z.string().trim().max(4000).optional(),
63
+ /*
64
+ * NOT `derived`. An earlier annotation here marked it server-owned on the
65
+ * strength of the comment above — and the form does render a picker for it
66
+ * (`ConsultationForm.tsx:92`), conditional on a `concerns` prop rather than
67
+ * on anything the schema can see. `kind: "list"` so the generator asks for a
68
+ * renderer instead of guessing, which is the honest answer for a control
69
+ * whose options arrive from outside the schema.
70
+ */
71
+ concern: annotate(z.array(z.string()).optional(), {
72
+ section: "who", order: 4, row: "full", kind: "list", label: "What do you need help with?",
73
+ }),
74
+ preferredDate: annotate(z.string().optional(), {
75
+ section: "schedule", sectionLabel: "When suits you",
76
+ order: 1, row: "pair", kind: "date", label: "Preferred date",
77
+ }),
78
+ preferredTime: annotate(z.string().optional(), {
79
+ section: "schedule", order: 2, row: "pair", label: "Preferred time",
80
+ }),
81
+ mode: annotate(consultationModeSchema.optional(), {
82
+ section: "schedule", order: 3, row: "pair", kind: "select", label: "How would you like to meet?",
83
+ }),
84
+ message: annotate(z.string().trim().max(4000).optional(), {
85
+ section: "detail", sectionLabel: "Anything else",
86
+ order: 1, row: "full", kind: "textarea", label: "Message",
87
+ }),
64
88
  });