@mohasinac/appkit 4.30.19 → 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 (417) hide show
  1. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  2. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  4. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -87
  5. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  6. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  7. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  8. package/dist/_internal/server/features/auth/permissions.js +3 -2
  9. package/dist/_internal/server/features/categories/data.js +15 -4
  10. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  11. package/dist/_internal/server/features/classified/index.js +0 -1
  12. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  13. package/dist/_internal/server/features/digital-code/index.js +0 -1
  14. package/dist/_internal/server/features/live/index.d.ts +0 -1
  15. package/dist/_internal/server/features/live/index.js +0 -1
  16. package/dist/_internal/server/features/products/data.js +15 -5
  17. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  18. package/dist/_internal/server/features/products/list-public.js +95 -7
  19. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  20. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  21. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  22. package/dist/_internal/server/features/tester/visibility.js +2 -2
  23. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  24. package/dist/_internal/server/functions/scheduled.js +27 -1
  25. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  26. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  27. package/dist/_internal/server/jobs/core/index.js +2 -0
  28. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  29. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  30. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  31. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  34. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  35. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  36. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  37. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  38. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  39. package/dist/_internal/shared/actions/action-registry.js +69 -22
  40. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  41. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  42. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  43. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  44. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  45. package/dist/_internal/shared/features/layout/types.js +19 -1
  46. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  47. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  48. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  49. package/dist/_internal/shared/features/offers/config.js +73 -2
  50. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  51. package/dist/_internal/shared/features/orders/schema.js +22 -2
  52. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  53. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  54. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  55. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  56. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  57. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  58. package/dist/client.d.ts +37 -20
  59. package/dist/client.js +36 -14
  60. package/dist/configs/next.js +10 -2
  61. package/dist/constants/api-endpoints.d.ts +18 -54
  62. package/dist/constants/api-endpoints.js +6 -26
  63. package/dist/constants/field-names.d.ts +6 -24
  64. package/dist/constants/field-names.js +10 -27
  65. package/dist/constants/geo/countries.d.ts +50 -0
  66. package/dist/constants/geo/countries.js +129 -0
  67. package/dist/constants/geo/subdivisions.d.ts +13 -0
  68. package/dist/constants/geo/subdivisions.js +104 -0
  69. package/dist/constants/index.d.ts +2 -2
  70. package/dist/constants/index.js +2 -2
  71. package/dist/constants/table-keys.d.ts +24 -0
  72. package/dist/constants/table-keys.js +28 -0
  73. package/dist/contracts/extend.d.ts +0 -9
  74. package/dist/contracts/index.d.ts +1 -1
  75. package/dist/contracts/realtime-session.js +7 -0
  76. package/dist/core/browser.d.ts +1 -1
  77. package/dist/core/browser.js +1 -1
  78. package/dist/core/index.d.ts +1 -1
  79. package/dist/core/index.js +1 -1
  80. package/dist/core/site-config.d.ts +0 -3
  81. package/dist/core/site-config.js +0 -3
  82. package/dist/errors/messages.d.ts +0 -8
  83. package/dist/errors/messages.js +0 -9
  84. package/dist/features/about/schemas/index.d.ts +8 -8
  85. package/dist/features/account/components/AddressFilters.js +32 -11
  86. package/dist/features/account/components/AddressForm.d.ts +26 -25
  87. package/dist/features/account/components/AddressForm.js +81 -103
  88. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  89. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  90. package/dist/features/account/components/UserSupportView.js +89 -58
  91. package/dist/features/account/components/index.d.ts +1 -7
  92. package/dist/features/account/components/index.js +0 -3
  93. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  94. package/dist/features/account/hooks/useAddresses.js +4 -6
  95. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  96. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  97. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  98. package/dist/features/account/index.d.ts +0 -3
  99. package/dist/features/account/index.js +0 -3
  100. package/dist/features/account/schemas/index.d.ts +0 -92
  101. package/dist/features/account/schemas/index.js +42 -20
  102. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  103. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  104. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  105. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  106. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  107. package/dist/features/addresses/schemas/address-form.js +212 -0
  108. package/dist/features/addresses/schemas/index.d.ts +1 -0
  109. package/dist/features/addresses/schemas/index.js +8 -0
  110. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  111. package/dist/features/admin/actions/index.d.ts +0 -1
  112. package/dist/features/admin/actions/index.js +0 -1
  113. package/dist/features/admin/actions/notification-actions.js +1 -41
  114. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  115. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  116. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  117. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  118. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  119. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  120. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  121. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  122. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  123. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  124. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  125. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  126. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  127. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  128. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  129. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  130. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  131. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  132. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  133. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  134. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  135. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  136. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  137. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  138. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  139. package/dist/features/admin/components/index.d.ts +2 -2
  140. package/dist/features/admin/components/index.js +1 -1
  141. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  142. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  143. package/dist/features/admin/index.d.ts +0 -3
  144. package/dist/features/admin/index.js +0 -2
  145. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  146. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  147. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  148. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  149. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  150. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  151. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  152. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  153. package/dist/features/admin/schemas/firestore.js +15 -151
  154. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  155. package/dist/features/admin/server.d.ts +0 -4
  156. package/dist/features/admin/server.js +0 -2
  157. package/dist/features/admin/types/index.d.ts +0 -7
  158. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  159. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  160. package/dist/features/auth/actions/index.d.ts +0 -1
  161. package/dist/features/auth/actions/index.js +0 -1
  162. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  163. package/dist/features/auth/auth-helpers.js +5 -7
  164. package/dist/features/auth/components/Guards.js +2 -1
  165. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  166. package/dist/features/auth/components/LoginForm.js +1 -3
  167. package/dist/features/auth/components/RegisterForm.js +62 -19
  168. package/dist/features/auth/permissions/constants.d.ts +27 -1
  169. package/dist/features/auth/role-predicates.d.ts +75 -7
  170. package/dist/features/auth/role-predicates.js +28 -2
  171. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  172. package/dist/features/auth/schemas/index.d.ts +1 -1
  173. package/dist/features/auth/schemas/index.js +44 -8
  174. package/dist/features/auth/types/index.d.ts +14 -1
  175. package/dist/features/blog/api/route.js +10 -2
  176. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  177. package/dist/features/blog/hooks/useBlog.js +2 -2
  178. package/dist/features/blog/schemas/index.d.ts +4 -0
  179. package/dist/features/blog/schemas/index.js +2 -0
  180. package/dist/features/blog/types/index.d.ts +1 -1
  181. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  182. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  183. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  184. package/dist/features/catalogue/schemas/validation.js +47 -10
  185. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  186. package/dist/features/categories/schemas/bundle-form.js +149 -0
  187. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  188. package/dist/features/classified/components/ClassifiedDetailPageView.js +37 -16
  189. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  190. package/dist/features/consultation/schemas/index.js +34 -10
  191. package/dist/features/corporate/schemas/index.js +36 -15
  192. package/dist/features/filters/FilterPanel.js +3 -3
  193. package/dist/features/filters/RangeFilter.d.ts +10 -0
  194. package/dist/features/filters/RangeFilter.js +41 -4
  195. package/dist/features/homepage/components/MarketplaceHomepageView.js +24 -5
  196. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  197. package/dist/features/layout/BottomActions.js +84 -11
  198. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  199. package/dist/features/layout/BottomActionsContext.js +1 -1
  200. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  201. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  202. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  203. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  204. package/dist/features/lottery/schemas/config-write.js +69 -9
  205. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  206. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  207. package/dist/features/orders/schemas/index.d.ts +2 -2
  208. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  209. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  210. package/dist/features/products/components/MakeOfferButton.js +33 -11
  211. package/dist/features/products/components/ProductDetailActions.js +0 -1
  212. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  213. package/dist/features/products/components/ProductDetailPageView.js +8 -5
  214. package/dist/features/products/constants/action-defs.d.ts +1 -2
  215. package/dist/features/products/constants/action-defs.js +40 -43
  216. package/dist/features/products/repository/products.repository.d.ts +5 -1
  217. package/dist/features/products/repository/products.repository.js +23 -1
  218. package/dist/features/products/schemas/firestore.d.ts +7 -3
  219. package/dist/features/products/schemas/product-features.validators.js +3 -0
  220. package/dist/features/promotions/components/CouponCard.js +8 -7
  221. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  222. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  223. package/dist/features/scams/schemas/report-form.js +18 -3
  224. package/dist/features/search/action-index/derive.d.ts +49 -0
  225. package/dist/features/search/action-index/derive.js +121 -0
  226. package/dist/features/search/action-index/repository.d.ts +42 -0
  227. package/dist/features/search/action-index/repository.js +100 -0
  228. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  229. package/dist/features/search/action-index/settings-entries.js +268 -0
  230. package/dist/features/search/action-index/types.d.ts +81 -0
  231. package/dist/features/search/action-index/types.js +102 -0
  232. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  233. package/dist/features/search/action-index/validate-entry.js +114 -0
  234. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  235. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  236. package/dist/features/search/repository/search.repository.js +3 -1
  237. package/dist/features/seller/actions/offer-actions.js +43 -24
  238. package/dist/features/seller/actions/seller-actions.js +29 -5
  239. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  240. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  241. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  242. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  243. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  244. package/dist/features/seller/components/SellerArtView.js +4 -119
  245. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  246. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  247. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  248. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  249. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  250. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  251. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  252. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  253. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  254. package/dist/features/seller/components/SellerLiveView.js +4 -126
  255. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  256. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  257. package/dist/features/seller/components/SellerStickersView.js +4 -119
  258. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  259. package/dist/features/seller/components/SellerSupportView.js +100 -0
  260. package/dist/features/seller/components/index.d.ts +4 -8
  261. package/dist/features/seller/components/index.js +2 -4
  262. package/dist/features/seller/index.d.ts +0 -1
  263. package/dist/features/seller/index.js +0 -1
  264. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  265. package/dist/features/seller/schemas/coupon-form.js +155 -23
  266. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  267. package/dist/features/seller/schemas/offer-forms.js +28 -12
  268. package/dist/features/shell/FormShell.js +2 -2
  269. package/dist/features/shell/QuickFormDrawer.js +2 -2
  270. package/dist/features/shell/SectionForm.d.ts +19 -1
  271. package/dist/features/shell/SectionForm.js +104 -17
  272. package/dist/features/shell/build-sections.d.ts +23 -0
  273. package/dist/features/shell/build-sections.js +106 -8
  274. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  275. package/dist/features/shell/field-ui-meta.js +14 -7
  276. package/dist/features/shell/form-sections.d.ts +46 -0
  277. package/dist/features/shell/form-sections.js +114 -0
  278. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  279. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  280. package/dist/features/shipments/schemas/validation.js +44 -13
  281. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  282. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  283. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  284. package/dist/features/stores/actions/store-query-actions.js +3 -2
  285. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  286. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  287. package/dist/features/support/repository/support.repository.d.ts +21 -0
  288. package/dist/features/support/repository/support.repository.js +63 -0
  289. package/dist/features/support/schemas/firestore.d.ts +27 -2
  290. package/dist/features/support/schemas/firestore.js +2 -0
  291. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  292. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  293. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  294. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  295. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  296. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  297. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  298. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  299. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  300. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +275 -29
  301. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +48 -12
  302. package/dist/http/api-handler.js +2 -1
  303. package/dist/index.d.ts +24 -103
  304. package/dist/index.js +33 -141
  305. package/dist/jobs.d.ts +1 -1
  306. package/dist/jobs.js +1 -1
  307. package/dist/monitoring/browser.d.ts +0 -2
  308. package/dist/monitoring/browser.js +12 -1
  309. package/dist/monitoring/index.d.ts +0 -2
  310. package/dist/monitoring/index.js +0 -1
  311. package/dist/next/api/routeHandler.js +6 -3
  312. package/dist/next/index.d.ts +1 -2
  313. package/dist/next/index.js +0 -1
  314. package/dist/next/middleware/index.d.ts +0 -2
  315. package/dist/next/middleware/index.js +0 -1
  316. package/dist/next/routing/route-map.d.ts +8 -7
  317. package/dist/next/routing/route-map.js +10 -3
  318. package/dist/providers/db-firebase/admin.js +15 -1
  319. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  320. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  321. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  322. package/dist/react/hooks/usePendingFilters.js +5 -3
  323. package/dist/react/hooks/usePendingTable.js +15 -3
  324. package/dist/repositories/index.d.ts +0 -1
  325. package/dist/repositories/index.js +0 -1
  326. package/dist/schemas/registry.d.ts +197 -232
  327. package/dist/schemas/registry.js +5 -6
  328. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  329. package/dist/security/authorization.d.ts +9 -0
  330. package/dist/security/authorization.js +16 -2
  331. package/dist/security/browser.d.ts +1 -3
  332. package/dist/security/browser.js +5 -2
  333. package/dist/security/index.d.ts +1 -3
  334. package/dist/security/index.js +19 -2
  335. package/dist/security/pii-encrypt.js +16 -0
  336. package/dist/security/pii-schemas.d.ts +0 -14
  337. package/dist/security/pii-schemas.js +7 -15
  338. package/dist/seed/_helpers/category-forest.js +2 -2
  339. package/dist/seed/_helpers/media.d.ts +19 -0
  340. package/dist/seed/_helpers/media.js +75 -0
  341. package/dist/seed/blog-posts-seed-data.js +17 -17
  342. package/dist/seed/carousel-slides-seed-data.js +7 -7
  343. package/dist/seed/cart-seed-data.js +13 -13
  344. package/dist/seed/categories-seed-data.js +10 -10
  345. package/dist/seed/events-seed-data.js +24 -24
  346. package/dist/seed/factories/product.factory.js +4 -4
  347. package/dist/seed/factories/store.factory.js +3 -3
  348. package/dist/seed/factories/user.factory.js +2 -2
  349. package/dist/seed/grouped-listings-seed-data.js +11 -11
  350. package/dist/seed/homepage-sections-seed-data.js +2 -2
  351. package/dist/seed/index.d.ts +0 -1
  352. package/dist/seed/index.js +0 -1
  353. package/dist/seed/manifest.d.ts +1 -1
  354. package/dist/seed/manifest.js +0 -5
  355. package/dist/seed/notifications-seed-data.js +20 -1
  356. package/dist/seed/orders-seed-data.js +2 -2
  357. package/dist/seed/products-art-seed-data.js +16 -16
  358. package/dist/seed/products-auctions-seed-data.js +34 -34
  359. package/dist/seed/products-classifieds-seed-data.js +35 -37
  360. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  361. package/dist/seed/products-live-items-seed-data.js +11 -11
  362. package/dist/seed/products-preorders-seed-data.js +13 -13
  363. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  364. package/dist/seed/products-standard-seed-data.js +54 -54
  365. package/dist/seed/products-stickers-seed-data.js +15 -15
  366. package/dist/seed/reviews-seed-data.js +4 -4
  367. package/dist/seed/scammers-seed-data.js +3 -3
  368. package/dist/seed/site-settings-seed-data.js +11 -24
  369. package/dist/seed/store-extensions-seed-data.js +10 -10
  370. package/dist/seed/stores-seed-data.js +11 -11
  371. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  372. package/dist/seed/support-tickets-seed-data.js +20 -1
  373. package/dist/seed/users-seed-data.js +20 -20
  374. package/dist/server-entry.d.ts +0 -1
  375. package/dist/server-entry.js +0 -1
  376. package/dist/server.d.ts +9 -6
  377. package/dist/server.js +7 -13
  378. package/dist/styles.css +248 -68
  379. package/dist/tailwind-utilities.css +1 -1
  380. package/dist/tokens/themes/default-dark.js +2 -2
  381. package/dist/tokens/themes/default-light.js +2 -2
  382. package/dist/tokens/tokens.css +24 -4
  383. package/dist/ui/components/ActionRow.d.ts +50 -0
  384. package/dist/ui/components/ActionRow.js +53 -0
  385. package/dist/ui/components/ActionRow.style.css +145 -0
  386. package/dist/ui/components/Drawer.d.ts +10 -0
  387. package/dist/ui/components/Drawer.js +2 -1
  388. package/dist/ui/components/FilterDrawer.js +4 -1
  389. package/dist/ui/components/FilterDrawer.style.css +6 -12
  390. package/dist/ui/components/FormActionBar.js +14 -1
  391. package/dist/ui/components/FormActionBar.style.css +12 -11
  392. package/dist/ui/components/ListingLayout.js +8 -1
  393. package/dist/ui/components/ListingLayout.style.css +6 -13
  394. package/dist/ui/components/Modal.d.ts +15 -1
  395. package/dist/ui/components/Modal.js +17 -3
  396. package/dist/ui/components/QuickCreateModal.js +2 -2
  397. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  398. package/dist/ui/components/RecordDetailModal.js +2 -2
  399. package/dist/ui/components/SideDrawer.d.ts +8 -0
  400. package/dist/ui/components/SideDrawer.js +2 -1
  401. package/dist/ui/components/SideDrawer.style.css +6 -3
  402. package/dist/ui/components/SideModal.d.ts +11 -1
  403. package/dist/ui/components/SideModal.js +3 -2
  404. package/dist/ui/components/SideModal.style.css +14 -0
  405. package/dist/ui/components/Typography.style.css +33 -24
  406. package/dist/ui/components/index.style.css +1 -0
  407. package/dist/ui/forms/FormShell.style.css +260 -311
  408. package/dist/ui/index.d.ts +2 -0
  409. package/dist/ui/index.js +1 -0
  410. package/dist/utils/index.d.ts +0 -1
  411. package/dist/utils/index.js +0 -1
  412. package/dist/validation/index.d.ts +1 -1
  413. package/dist/validation/index.js +1 -1
  414. package/dist/validation/schemas.d.ts +0 -19
  415. package/dist/validation/schemas.js +8 -19
  416. package/package.json +1 -1
  417. package/scripts/seed-cli.mjs +3 -6
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { annotate } from "../../shell/field-ui-meta";
2
3
  const productConditionSchema = z.enum([
3
4
  "new",
4
5
  "like_new",
@@ -11,15 +12,51 @@ const productConditionSchema = z.enum([
11
12
  "graded",
12
13
  ]);
13
14
  export const createCatalogueItemSchema = z.object({
14
- title: z.string().min(1, "Title is required"),
15
- description: z.string().optional(),
16
- images: z.array(z.string()).default([]),
17
- mainImage: z.string().optional(),
18
- condition: productConditionSchema.optional(),
19
- categorySlugs: z.array(z.string()).optional(),
20
- brandSlug: z.string().optional(),
21
- price: z.number().min(0).optional(),
22
- quantity: z.number().int().min(1).default(1),
23
- visibility: z.enum(["public", "private"]).default("public"),
15
+ title: annotate(z.string().min(1, "Title is required"), {
16
+ section: "basics", sectionLabel: "Item", sectionRequired: true,
17
+ order: 1, row: "full", label: "Title",
18
+ }),
19
+ description: annotate(z.string().optional(), {
20
+ section: "basics", order: 2, row: "full", kind: "textarea", label: "Description",
21
+ }),
22
+ condition: annotate(productConditionSchema.optional(), {
23
+ section: "basics", order: 3, row: "pair", kind: "select", label: "Condition",
24
+ }),
25
+ quantity: annotate(z.number().int().min(1).default(1), {
26
+ section: "basics", order: 4, row: "pair", kind: "number", label: "Quantity",
27
+ }),
28
+ /*
29
+ * `kind: "media"` is inferred from the field NAME (field-ui-meta.ts:336),
30
+ * which also sets `sectionKeepMounted` — mandatory here, because a collapsed
31
+ * section unmounts and would discard an in-flight upload. Declared anyway so
32
+ * the section id and order are not left to inference.
33
+ */
34
+ images: annotate(z.array(z.string()).default([]), {
35
+ section: "media", sectionLabel: "Photos", order: 1, row: "full", kind: "media",
36
+ sectionKeepMounted: true, label: "Photos",
37
+ }),
38
+ /*
39
+ * Derived from `images[0]`, never typed. Rendering it as an editable box
40
+ * lets a user point it at a photo they have since removed from the gallery —
41
+ * the field would then name an image that does not exist on the item.
42
+ */
43
+ mainImage: annotate(z.string().optional(), {
44
+ section: "media", order: 2, row: "full", kind: "media", label: "Main photo",
45
+ derived: true, tier: "t1-derive", help: "The first photo in the gallery.",
46
+ }),
47
+ categorySlugs: annotate(z.array(z.string()).optional(), {
48
+ section: "classification", sectionLabel: "Filing", order: 1, row: "pair",
49
+ label: "Categories",
50
+ }),
51
+ brandSlug: annotate(z.string().optional(), {
52
+ section: "classification", order: 2, row: "pair", kind: "select", label: "Brand",
53
+ }),
54
+ price: annotate(z.number().min(0).optional(), {
55
+ section: "pricing", sectionLabel: "Value", order: 1, row: "pair", kind: "number",
56
+ label: "Estimated value (₹)",
57
+ }),
58
+ visibility: annotate(z.enum(["public", "private"]).default("public"), {
59
+ section: "visibility", order: 1, row: "pair", kind: "select", label: "Visibility",
60
+ }),
24
61
  });
25
62
  export const updateCatalogueItemSchema = createCatalogueItemSchema.partial();
@@ -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
+ });
@@ -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,11 +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
23
  import { safeRead } from "../../../errors/safe-read";
26
- import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities";
27
- import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config";
24
+ import { DEFAULT_MIN_OFFER_PERCENT, offersEnabledFor, resolveOfferBounds, } from "../../../_internal/shared/features/offers/config";
28
25
  import { ListingBottomActions } from "../../products/components/ListingBottomActions";
29
26
  function toDescriptionHtml(raw) {
30
27
  if (!raw)
@@ -68,31 +65,55 @@ export async function ClassifiedDetailPageView({ slug, initialProduct, renderOff
68
65
  fallback: null,
69
66
  }),
70
67
  ]);
71
- // Classifieds are the one non-standard type where haggling is the norm, so
72
- // `canMakeOffer` is true for them — but the seller still opts in per listing
73
- // (`allowOffers`, usually alongside `classified.negotiable`).
74
- const offersAvailable = siteSettings?.featureFlags?.offers !== false &&
75
- canMakeOffer("classified") &&
76
- p.allowOffers === true &&
77
- price !== null &&
78
- !!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;
79
91
  return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: String(p.id ?? p.slug ?? ""), productType: "product", snapshot: {
80
92
  title,
81
93
  thumb: images[0],
82
94
  price: price ?? undefined,
83
95
  storeId: typeof p.storeId === "string" ? p.storeId : undefined,
84
96
  storeName: storeName ?? undefined,
85
- } }), _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) => ({
86
98
  id: s.id,
87
99
  label: s.title,
88
100
  content: _jsx(CustomSectionTabContent, { section: s }),
89
- })) })), renderBuyBar: () => (_jsxs(Stack, { id: "classified-contact-bar", className: "p-[var(--appkit-space-5)]", border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [offersAvailable &&
90
- 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({
91
102
  productId: String(product.id),
92
103
  price: price,
93
104
  currency,
94
105
  minOfferPercent: typeof p.minOfferPercent === "number"
95
106
  ? p.minOfferPercent
96
107
  : DEFAULT_MIN_OFFER_PERCENT,
97
- }), _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 })] })] }));
98
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
  });