@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
  export const corporateInquiryStatusSchema = z.enum([
3
4
  "pending",
4
5
  "contacted",
@@ -37,23 +38,30 @@ export const corporateInquirySchema = z.object({
37
38
  * Form schema for submitting a corporate inquiry.
38
39
  */
39
40
  export const submitCorporateInquirySchema = z.object({
40
- companyName: z.string().trim().min(2, "A company name is required.").max(160),
41
- contactPerson: z.string().trim().min(2, "Who should we reply to?").max(120),
42
- designation: z.string().trim().max(120).optional(),
43
- email: z
41
+ companyName: annotate(z.string().trim().min(2, "A company name is required.").max(160), {
42
+ section: "who", sectionLabel: "Your company", sectionRequired: true,
43
+ order: 1, row: "pair", label: "Company name",
44
+ }),
45
+ contactPerson: annotate(z.string().trim().min(2, "Who should we reply to?").max(120), {
46
+ section: "who", order: 2, row: "pair", label: "Contact person",
47
+ }),
48
+ designation: annotate(z.string().trim().max(120).optional(), {
49
+ section: "who", order: 3, row: "pair", label: "Designation",
50
+ }),
51
+ email: annotate(z
44
52
  .string()
45
53
  .trim()
46
54
  .toLowerCase()
47
55
  .min(1, "An email address is required.")
48
- .email("That doesn't look like an email address."),
56
+ .email("That doesn't look like an email address."), { section: "who", order: 4, row: "pair", inputType: "email", label: "Email" }),
49
57
  // Indian mobile, tolerant of spacing and a country code. Optional — a
50
58
  // reachable email is enough, and demanding a phone number loses enquiries.
51
- phone: z
59
+ phone: annotate(z
52
60
  .string()
53
61
  .trim()
54
62
  .regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
55
63
  .optional()
56
- .or(z.literal("")),
64
+ .or(z.literal("")), { section: "who", order: 5, row: "pair", inputType: "tel", label: "Phone" }),
57
65
  /*
58
66
  * 🛑 COERCED. These were `z.number()`, and this schema's own form sends
59
67
  * strings — so every submission carrying a unit count failed to parse the
@@ -62,19 +70,32 @@ export const submitCorporateInquirySchema = z.object({
62
70
  *
63
71
  * A sales reply is priced from these two, and until now both were free text.
64
72
  */
65
- units: z.coerce
73
+ units: annotate(z.coerce
66
74
  .number({ invalid_type_error: "Enter the number of units as a number." })
67
75
  .int("Units must be a whole number.")
68
76
  .positive("At least one unit.")
69
77
  .max(1000000, "That looks like a typo — contact us directly for orders this large.")
70
- .optional(),
71
- budgetPerUnit: z.coerce
78
+ .optional(), { section: "pricing", sectionLabel: "What you need", order: 1, row: "pair", kind: "number", label: "Units" }),
79
+ budgetPerUnit: annotate(z.coerce
72
80
  .number({ invalid_type_error: "Enter a per-unit budget as a number." })
73
81
  .min(0, "A budget cannot be negative.")
74
82
  .max(10000000, "That looks like a typo.")
75
- .optional(),
76
- totalBudget: z.coerce.number().min(0).optional(),
77
- deliveryDateRequired: z.string().optional(),
78
- customBranding: z.boolean().default(false),
79
- message: z.string().trim().max(4000).optional(),
83
+ .optional(), { section: "pricing", order: 2, row: "pair", kind: "number", label: "Budget per unit (₹)" }),
84
+ // Computed from units × budgetPerUnit on the client, so it is shown as a
85
+ // live read-only preview rather than asked for twice.
86
+ totalBudget: annotate(z.coerce.number().min(0).optional(), {
87
+ section: "pricing", order: 3, row: "pair", kind: "number", label: "Total budget (₹)",
88
+ derived: true, tier: "t1-derive",
89
+ }),
90
+ deliveryDateRequired: annotate(z.string().optional(), {
91
+ section: "schedule", sectionLabel: "Timeline", order: 1, row: "pair", kind: "date",
92
+ label: "Delivery date required by",
93
+ }),
94
+ customBranding: annotate(z.boolean().default(false), {
95
+ section: "schedule", order: 2, row: "full", kind: "toggle", label: "We need custom branding",
96
+ }),
97
+ message: annotate(z.string().trim().max(4000).optional(), {
98
+ section: "detail", sectionLabel: "Anything else", order: 1, row: "full", kind: "textarea",
99
+ label: "Message",
100
+ }),
80
101
  });
@@ -5,6 +5,7 @@ import { Div } from "../../ui";
5
5
  import { FilterFacetSection } from "./FilterFacetSection";
6
6
  import { RangeFilter } from "./RangeFilter";
7
7
  import { SwitchFilter } from "./SwitchFilter";
8
+ import { joinFilterValues, splitFilterValues } from "../../constants/table-keys";
8
9
  export function FilterPanel({ config, table, className = "", }) {
9
10
  const [expandedIndex, setExpandedIndex] = useState(() => config.findIndex((c) => !c.defaultCollapsed));
10
11
  const handleToggle = (idx) => setExpandedIndex((prev) => (prev === idx ? null : idx));
@@ -17,9 +18,8 @@ export function FilterPanel({ config, table, className = "", }) {
17
18
  return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "single", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, vals[0] ?? "") }, `${item.type}-${item.key}`));
18
19
  }
19
20
  case "facet-multi": {
20
- const raw = table.get(item.key);
21
- const selected = raw ? raw.split(",").filter(Boolean) : [];
22
- return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "multi", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, vals.join(",")) }, `${item.type}-${item.key}`));
21
+ const selected = splitFilterValues(table.get(item.key));
22
+ return (_jsx(FilterFacetSection, { title: item.title, options: item.options, selectionMode: "multi", selected: selected, defaultCollapsed: item.defaultCollapsed, searchable: item.searchable, isOpen: isOpen, onToggle: () => handleToggle(idx), onClear: () => table.set(item.key, ""), onChange: (vals) => table.set(item.key, joinFilterValues(vals)) }, `${item.type}-${item.key}`));
23
23
  }
24
24
  case "switch": {
25
25
  const checked = table.get(item.key) === "true";
@@ -18,6 +18,16 @@ export interface RangeFilterProps {
18
18
  className?: string;
19
19
  isOpen?: boolean;
20
20
  onToggle?: () => void;
21
+ /**
22
+ * Override the clear behaviour. **Rarely needed** — clearing a range is
23
+ * `onMinChange("") + onMaxChange("")`, which is now the default, so the X
24
+ * appears on every range filter without a call site opting in.
25
+ *
26
+ * It used to be required-in-practice: 18 of the 19 call sites passed nothing
27
+ * and therefore rendered no clear button at all, leaving a range you could
28
+ * set and not unset. Pass this only when clearing must ALSO reset something
29
+ * outside the two values (`FilterPanel` clears its own pending map).
30
+ */
21
31
  onClear?: () => void;
22
32
  }
23
33
  export declare function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onMinChange, onMaxChange, type, prefix, minPlaceholder, maxPlaceholder, defaultCollapsed, showSlider, minBound, maxBound, step, className, isOpen: controlledOpen, onToggle, onClear, }: RangeFilterProps): import("react").JSX.Element;
@@ -5,10 +5,41 @@ import { Button, Div, Input, Row, Slider, Span, Stack, Text } from "../../ui";
5
5
  import { cn } from "./filterUtils";
6
6
  const CLS_BADGE_COUNT = "inline-flex items-center justify-center w-5 h-5 rounded-full bg-success-surface dark:bg-success-surface text-success dark:text-success ring-1 ring-success/20";
7
7
  const CLS_CLEAR_BTN = "inline-flex items-center justify-center w-5 h-5 text-[var(--appkit-color-text-muted)] hover:text-error dark:hover:text-error rounded-full transition-colors";
8
+ /**
9
+ * Parse a filter value, distinguishing "not set" from a legitimate zero.
10
+ *
11
+ * 🛑 NOT `parseFloat(v) || bound`. `parseFloat("")` is NaN and falsy, which is
12
+ * the intended case — but so is `parseFloat("0")`, so a user asking for a max
13
+ * of 0 got the 500000 bound instead, and the filter they could see in the
14
+ * inputs was not the one being applied.
15
+ */
16
+ function rawNum(value) {
17
+ const n = Number.parseFloat(value);
18
+ return Number.isFinite(n) ? n : null;
19
+ }
8
20
  function DualSlider({ minValue, maxValue, onMinChange, onMaxChange, minBound, maxBound, step = 1, prefix = "", }) {
9
- const minNum = Math.max(minBound, Math.min(parseFloat(minValue) || minBound, maxBound));
10
- const maxNum = Math.min(maxBound, Math.max(parseFloat(maxValue) || maxBound, minBound));
11
- return (_jsxs(Stack, { gap: "sm", className: "w-full", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, minNum] }), _jsx(Span, { size: "xs", color: "muted", children: "-" }), _jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, maxNum] })] }), _jsxs(Stack, { gap: "xs", "aria-hidden": "true", children: [_jsx(Slider, { value: minNum, min: minBound, max: Math.max(minBound, maxNum - step), step: step, onChange: (v) => onMinChange(String(v)) }), _jsx(Slider, { value: maxNum, min: Math.min(maxBound, minNum + step), max: maxBound, step: step, onChange: (v) => onMaxChange(String(v)) })] }), _jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "xs", color: "muted", children: [prefix, minBound] }), _jsxs(Span, { size: "xs", color: "muted", children: [prefix, maxBound] })] })] }));
21
+ const minRaw = rawNum(minValue);
22
+ const maxRaw = rawNum(maxValue);
23
+ /*
24
+ * Display only. An unset side shows its bound so the track reads sensibly —
25
+ * but the bound is never what gets WRITTEN, which is the distinction the old
26
+ * `|| bound` collapsed.
27
+ */
28
+ const minNum = Math.min(Math.max(minRaw ?? minBound, minBound), maxBound);
29
+ const maxNum = Math.max(Math.min(maxRaw ?? maxBound, maxBound), minBound);
30
+ /*
31
+ * Each slider spans the FULL range and clamps itself on write.
32
+ *
33
+ * It used to derive its opposite end from the other thumb — the min slider's
34
+ * `max` was `maxNum - step` — so with max unset, `maxNum` collapsed to the
35
+ * bound, and any later change to one side silently re-scaled and appeared to
36
+ * reset the other. Clamping on write instead of on range means dragging one
37
+ * thumb can never move or clear the other, and a value that would cross is
38
+ * pinned rather than the whole control rescaling underneath the user.
39
+ */
40
+ const writeMin = (v) => onMinChange(String(Math.min(v, maxRaw ?? maxBound)));
41
+ const writeMax = (v) => onMaxChange(String(Math.max(v, minRaw ?? minBound)));
42
+ return (_jsxs(Stack, { gap: "sm", className: "w-full", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, minNum] }), _jsx(Span, { size: "xs", color: "muted", children: "-" }), _jsxs(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-primary-600 dark:text-secondary-400", children: [prefix, maxNum] })] }), _jsxs(Stack, { gap: "xs", "aria-hidden": "true", children: [_jsx(Slider, { value: minNum, min: minBound, max: maxBound, step: step, onChange: writeMin }), _jsx(Slider, { value: maxNum, min: minBound, max: maxBound, step: step, onChange: writeMax })] }), _jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Span, { size: "xs", color: "muted", children: [prefix, minBound] }), _jsxs(Span, { size: "xs", color: "muted", children: [prefix, maxBound] })] })] }));
12
43
  }
13
44
  export function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onMinChange, onMaxChange, type = "number", prefix, minPlaceholder, maxPlaceholder, defaultCollapsed = true, showSlider = false, minBound, maxBound, step = 1, className = "", isOpen: controlledOpen, onToggle, onClear, }) {
14
45
  const isControlled = controlledOpen !== undefined;
@@ -25,6 +56,12 @@ export function RangeFilter({ title, minLabel, maxLabel, minValue, maxValue, onM
25
56
  minBound !== undefined &&
26
57
  maxBound !== undefined;
27
58
  const hasValue = !!(minValue || maxValue);
59
+ // Clearing a range needs nothing a caller has to supply. See `onClear` above.
60
+ const handleClear = onClear ??
61
+ (() => {
62
+ onMinChange("");
63
+ onMaxChange("");
64
+ });
28
65
  const inputClass = "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text)] focus:outline-none focus:ring-2 focus:ring-primary-500/20 ";
29
- return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), onClear && hasValue && (_jsx(Button, { type: "button", onClick: onClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
66
+ return (_jsxs(Div, { role: "group", "aria-labelledby": `rf-${title}`, className: cn("p-[var(--appkit-space-4)] border-b border-[var(--appkit-color-border)] last:border-b-0", className), children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsxs(Button, { type: "button", id: `rf-${title}`, onClick: handleToggle, variant: "ghost", size: "sm", className: "flex-1 justify-[space-between] text-[0.875rem] font-[600] text-[var(--appkit-color-text)] py-[0.25rem] hover:opacity-80 transition-opacity", "aria-expanded": !isCollapsed, children: [_jsxs(Span, { layout: "flex", gap: "md", children: [title, hasValue && (_jsx(Span, { size: "xs", className: CLS_BADGE_COUNT, children: "1" }))] }), _jsx("svg", { className: cn("w-4 h-4 shrink-0 text-[var(--appkit-color-text-muted)] transition-transform duration-200", isCollapsed ? "" : "rotate-180"), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), hasValue && (_jsx(Button, { type: "button", onClick: handleClear, variant: "ghost", size: "sm", className: CLS_CLEAR_BTN, "aria-label": "Clear", children: _jsx("svg", { className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), !isCollapsed && (_jsxs(Stack, { gap: "3", className: "mt-3", children: [canShowSlider && (_jsx(DualSlider, { minValue: minValue, maxValue: maxValue, onMinChange: onMinChange, onMaxChange: onMaxChange, minBound: minBound, maxBound: maxBound, step: step, prefix: prefix })), _jsxs(Row, { align: "end", gap: "sm", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [minLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: minLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: minValue, onChange: (e) => onMinChange(e.target.value), placeholder: minPlaceholder ?? (type === "date" ? "From" : "Min"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] }), _jsx(Span, { size: "xs", className: "flex-shrink-0 pb-[0.375rem]", color: "faint", children: "-" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [maxLabel && (_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: maxLabel })), _jsxs(Div, { className: "relative", children: [prefix && (_jsx(Span, { size: "xs", className: "absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none", color: "faint", children: prefix })), _jsx(Input, { type: type, value: maxValue, onChange: (e) => onMaxChange(e.target.value), placeholder: maxPlaceholder ?? (type === "date" ? "To" : "Max"), className: cn(inputClass, prefix ? "pl-5" : "") })] })] })] })] }))] }));
30
67
  }
@@ -7,7 +7,17 @@ import { safeRead } from "../../../errors/safe-read";
7
7
  import { fetchLiveStats } from "../lib/live-stats";
8
8
  import { renderSection, AnnouncementBar } from "../lib/section-renderer";
9
9
  import { homepageSectionsRepository } from "../repository/homepage-sections.repository";
10
- import { filterSectionsByFeatureFlags } from "../../../_internal/shared/features/homepage/section-gate";
10
+ import { isListingTypeEnabled } from "../../../_internal/shared/listing-types/feature-flags";
11
+ /**
12
+ * Homepage sections that advertise a specific listing type, and must therefore
13
+ * disappear when the site stops offering it. A section type absent from this
14
+ * map always renders.
15
+ */
16
+ const HOMEPAGE_SECTION_LISTING_TYPE = {
17
+ auctions: "auction",
18
+ "pre-orders": "pre-order",
19
+ "prize-draws": "prize-draw",
20
+ };
11
21
  import { getFeaturedProducts, getFeaturedAuctions, getFeaturedPreOrders } from "../../products/actions/product-actions";
12
22
  import { listTopLevelCategories, listBrandCategories } from "../../categories/actions/category-actions";
13
23
  import { listFeaturedBundles } from "../../../_internal/server/features/bundles/data";
@@ -49,10 +59,19 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onB
49
59
  route: "/", key: "homepage.faqs", fallback: [],
50
60
  }),
51
61
  ]);
52
- // Respect the feature flags. This gate existed but lived inside a function
53
- // with no call sites, so until 2026-08-24 turning off `auctions` hid auctions
54
- // everywhere EXCEPT the homepage, which went on advertising them.
55
- const enabledSections = filterSectionsByFeatureFlags(allSections, siteSettings?.featureFlags);
62
+ // A homepage section must not advertise a listing type the site does not
63
+ // offer. This gate previously read `featureFlags.{auctions,preOrders,events,
64
+ // blog}`; that group was deleted on 2026-08-29, and only the listing-type
65
+ // half of it had a real successor (`settings.listings.listingTypes`).
66
+ //
67
+ // So auctions / pre-orders / prize-draws are gated by the same control that
68
+ // hides them on every other surface, and the `events` / `blog` sections are
69
+ // now unconditional — there is no product control for "we don't run events",
70
+ // and there never was one that anything but this line read.
71
+ const enabledSections = allSections.filter((section) => {
72
+ const gated = HOMEPAGE_SECTION_LISTING_TYPE[section.type];
73
+ return !gated || isListingTypeEnabled(gated, siteSettings);
74
+ });
56
75
  // Collect live metric requests from all enabled stats sections
57
76
  const liveStatRequests = [];
58
77
  for (const section of enabledSections) {
@@ -23,7 +23,7 @@ export function useBlogArticles(options) {
23
23
  return featuredResult;
24
24
  }
25
25
  const remaining = MIN_BLOG_COUNT - featured.length;
26
- const latestResult = await apiClient.get(`${BLOG_ENDPOINTS.SORTED("-publishedAt")}&perPage=${MIN_BLOG_COUNT + remaining}`);
26
+ const latestResult = await apiClient.get(`${BLOG_ENDPOINTS.SORTED("-publishedAt")}&pageSize=${MIN_BLOG_COUNT + remaining}`);
27
27
  const existingIds = new Set(featured.map((p) => p.id));
28
28
  const filler = latestResult.posts
29
29
  .filter((p) => !existingIds.has(p.id))
@@ -33,7 +33,7 @@ import { useState, useRef, useEffect, useId } from "react";
33
33
  import { X, ChevronUp, ChevronDown, Check } from "lucide-react";
34
34
  import { useBottomActionsContext } from "./BottomActionsContext";
35
35
  import { useClickOutside } from "../../react";
36
- import { Div, Row, Span, Text, Button } from "../../ui";
36
+ import { ActionRow, Div, Row, Span, Text, Button } from "../../ui";
37
37
  // Token values inlined from @mohasinac/appkit/tokens
38
38
  const BOTTOM_NAV_BG = "bg-[color-mix(in_srgb,var(--appkit-color-bg)_90%,transparent)] backdrop-blur-md border-t border-[var(--appkit-color-border)]";
39
39
  const BOTTOM_NAV_HEIGHT = "h-14";
@@ -88,21 +88,94 @@ function InfoPanel({ id, open, children, }) {
88
88
  .filter(Boolean)
89
89
  .join(" "), "aria-hidden": !open, children: _jsx(Div, { className: "max-h-[60vh]", overflow: "y-auto", padding: "md", children: children }) }));
90
90
  }
91
- function PageActionsRow({ pageActions, dispatchAction, }) {
92
- return (_jsx(_Fragment, { children: pageActions.map((action) => {
91
+ /**
92
+ * How many actions fit on one line before the row splits in two.
93
+ *
94
+ * 🛑 ROW COUNT is a function of `pageActions.length` and NOTHING else — never
95
+ * of validation state, and never of measured width. `BottomChrome`'s
96
+ * ResizeObserver faithfully republishes `--bottom-chrome-height` on any height
97
+ * change, and every consumer (BackToTop, the pagination bar, the footer's
98
+ * clearance) reflows with it. A bar that grew a row when an error appeared
99
+ * would shift the whole page while the user was typing. The error count lives
100
+ * in `infoLabel`, which sits in the absolutely-positioned panel and is excluded
101
+ * from the published height by construction.
102
+ *
103
+ * The invariant is about ROW COUNT, not pixel height. `ActionRow` lets a long
104
+ * label wrap to a second line, which changes the bar's height by ~16px,
105
+ * monotonically, and the observer handles that correctly. What must never
106
+ * happen is a row count that FLIPS: that is a ~48px step and it is reversible
107
+ * mid-interaction.
108
+ *
109
+ * Which is exactly why the count split stays primary and `flex-wrap` is only an
110
+ * overflow net. In this app LABELS ARE STATE — "Add to Cart" becomes "Adding…",
111
+ * "Buy Now" becomes "Out of Stock", "Apply" becomes "Apply (3)". If width alone
112
+ * decided rows, tapping Add to Cart would shorten the label, free ~40px,
113
+ * un-wrap a row and shift the page under the user's thumb at the exact moment
114
+ * they committed to the tap.
115
+ *
116
+ * 🛑 This constant is COUPLED to `--appkit-size-action-min` (tokens.css).
117
+ * Two actions per line is safe only while `2 × action-min + gap` fits the
118
+ * narrowest supported viewport (280px). Raising either without re-checking that
119
+ * sum reintroduces device-dependent row flipping.
120
+ */
121
+ const SINGLE_ROW_MAX_ACTIONS = 2;
122
+ function ActionButtons({ actions, dispatchAction, }) {
123
+ return (_jsx(_Fragment, { children: actions.map((action) => {
93
124
  const isIconOnly = !action.label;
94
- const growClass = isIconOnly || action.grow === false
95
- ? "flex-shrink-0 w-11"
96
- : "flex-1 min-w-0";
125
+ /*
126
+ * Width is ActionRow's job now, not this component's — every action is
127
+ * sized from its own label by `.appkit-action-row__group > *`, and only
128
+ * the leftover is shared out. There is deliberately NO per-action width
129
+ * class here: the old `grow` ternary is what produced a 44px stub
130
+ * reading "Ba" next to a full-width "Continue to payment".
131
+ *
132
+ * The one thing a stylesheet cannot infer is that a child has no label,
133
+ * so icon-only opts into its square tap target explicitly.
134
+ *
135
+ * 🛑 Never add `flex-1`, `flex-shrink-0`, `basis-*`, `w-*`, `h-*`,
136
+ * `truncate` or `leading-none` below. Both tailwind configs set
137
+ * `important: true`, so any of them beats ActionRow's CSS and silently
138
+ * reverts this bar to the squeezed behaviour with no visible error.
139
+ */
97
140
  return (_jsxs(Button, { type: "button", variant: action.variant ?? "primary", size: "sm", isLoading: action.loading, disabled: action.disabled, onClick: () => dispatchAction(action.id), className: [
98
- "h-10 relative",
99
- growClass,
100
- isIconOnly ? "px-[var(--appkit-space-0)] justify-center" : "",
141
+ "relative",
142
+ isIconOnly ? "appkit-action-row__icon-only justify-center" : "",
101
143
  ]
102
144
  .filter(Boolean)
103
- .join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { className: "truncate leading-none", children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-[var(--appkit-space-1)] pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
145
+ .join(" "), children: [action.icon && (_jsx(Span, { className: ["flex-shrink-0", action.label ? "mr-1.5" : ""].join(" "), "aria-hidden": "true", children: action.icon })), action.label && _jsx(Span, { children: action.label }), action.badge !== undefined && (_jsx(Span, { className: `absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] ${CLS_COUNT_BADGE} text-[10px] ${FLEX_CENTER} px-[var(--appkit-space-1)] pointer-events-none select-none`, rounded: "full", weight: "bold", "aria-hidden": "true", children: action.badge }))] }, action.id));
104
146
  }) }));
105
147
  }
148
+ /**
149
+ * Lays a form's or a page's actions out across one or two lines.
150
+ *
151
+ * | n | layout |
152
+ * |------|-----------------------------------------------------------------|
153
+ * | 1 | one row, full-width primary |
154
+ * | 2 | one row, each sized from its own label |
155
+ * | 3+ | two rows — secondaries above, primary full-width below |
156
+ *
157
+ * The LAST action is treated as the primary. That holds for every producer:
158
+ * `useFormBottomActions` appends submit last, and the checkout/cart bars build
159
+ * `[back, primary]`. Putting it on its own line keeps the thumb target the same
160
+ * size no matter how many secondary actions a screen has, which is the whole
161
+ * reason a 5-action form (3 forms + soft-ban + hard-ban on the admin user
162
+ * editor) was unrepresentable before.
163
+ *
164
+ * Secondaries WRAP rather than overflowing into a menu. Wrapping keeps the
165
+ * height a pure function of the action count — the invariant above — whereas a
166
+ * popover would add a second interaction to reach an action that is already
167
+ * only one line away. No current caller exceeds five.
168
+ */
169
+ function PageActionsRow({ pageActions, dispatchAction, }) {
170
+ if (pageActions.length === 0)
171
+ return null;
172
+ if (pageActions.length <= SINGLE_ROW_MAX_ACTIONS) {
173
+ return (_jsx(ActionRow, { children: _jsx(ActionButtons, { actions: pageActions, dispatchAction: dispatchAction }) }));
174
+ }
175
+ const secondaries = pageActions.slice(0, -1);
176
+ const primary = pageActions[pageActions.length - 1];
177
+ return (_jsxs(Div, { className: "flex w-full flex-col gap-[var(--appkit-space-2)]", children: [_jsx(ActionRow, { children: _jsx(ActionButtons, { actions: secondaries, dispatchAction: dispatchAction }) }), _jsx(ActionRow, { children: _jsx(ActionButtons, { actions: [primary], dispatchAction: dispatchAction }) })] }));
178
+ }
106
179
  export default function BottomActions() {
107
180
  const { state, actionCallbacksRef, bulkCallbacksRef, bulkClearRef } = useBottomActionsContext();
108
181
  const [pickerOpen, setPickerOpen] = useState(false);
@@ -204,7 +277,7 @@ export default function BottomActions() {
204
277
  isVisible ? "" : "pointer-events-none",
205
278
  ]
206
279
  .filter(Boolean)
207
- .join(" "), children: [isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { border: "subtle", className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b /80", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `${BOTTOM_NAV_HEIGHT}`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-surface active:bg-primary-200 dark:bg-primary-950/30 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
280
+ .join(" "), children: [isBulkMode && (_jsx(Div, { className: "h-[3px] w-full bg-[image:var(--appkit-gradient-brand-tri)]" })), secondaryLabel && !isBulkMode && (_jsx(Div, { className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b border-[var(--appkit-color-border-subtle)]", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: secondaryLabel }) })), infoLabel && !isBulkMode && !hasInfoPanel && (_jsx(Div, { className: "pt-[var(--appkit-space-2)] pb-[var(--appkit-space-0)] border-b border-[var(--appkit-color-border-subtle)]", padding: "x-md", children: _jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }) })), infoLabel && !isBulkMode && hasInfoPanel && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setInfoOpen((o) => !o), "aria-expanded": infoOpen, "aria-controls": infoPanelId, className: "w-full flex items-center justify-between gap-[var(--appkit-space-2)] rounded-none border-b border-[var(--appkit-color-border)]/80 px-[var(--appkit-space-4)] pt-[var(--appkit-space-2)] pb-[var(--appkit-space-1)] min-h-0", children: [_jsx(Text, { className: "leading-5 truncate", color: "muted", size: "xs", weight: "semibold", children: infoLabel }), _jsxs(Span, { className: "flex-shrink-0 flex items-center gap-[var(--appkit-space-1)]", children: [_jsx(Text, { as: "span", size: "xs", color: "muted", children: infoOpen ? "Hide" : "Details" }), infoOpen ? (_jsx(ChevronDown, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" })) : (_jsx(ChevronUp, { className: "w-4 h-4 text-zinc-400", "aria-hidden": "true" }))] })] })), _jsx(Row, { className: `min-${BOTTOM_NAV_HEIGHT} py-[var(--appkit-space-2)]`, gap: "sm", padding: "x-sm", children: isBulkMode && bulk ? (_jsxs(_Fragment, { children: [_jsxs(Button, { rounded: "full", gap: "xs", type: "button", variant: "ghost", onClick: dispatchBulkClear, className: "inline- flex-shrink-0 bg-primary-50 hover:bg-primary-surface active:bg-primary-200 dark:bg-primary-950/30 text-primary-700 dark:text-primary-300 pl-2 pr-3 h-8 border border-primary-200/70 dark:border-primary-800/50 transition-colors min-h-0", "aria-label": "Clear selection", children: [_jsx(X, { className: "w-3.5 h-3.5 flex-shrink-0", "aria-hidden": "true" }), _jsx(Span, { size: "xs", weight: "semibold", className: "tabular-nums whitespace-nowrap leading-none", children: bulk.noun
208
281
  ? `${bulk.selectedCount} ${bulk.noun}`
209
282
  : `${bulk.selectedCount} selected` })] }), bulkActions.length > 0 && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setPickerOpen((o) => !o), "aria-haspopup": "listbox", "aria-expanded": pickerOpen, className: [
210
283
  "flex-1 min-w-0 h-10 flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-3)] rounded-lg border text-[length:var(--appkit-text-sm)] font-medium transition-colors",
@@ -14,7 +14,7 @@
14
14
  * ```tsx
15
15
  * useBottomActions({
16
16
  * actions: [
17
- * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", grow: false, onClick: handleWishlist },
17
+ * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", onClick: handleWishlist },
18
18
  * { id: "cart", label: t("addToCart"), variant: "outline", onClick: handleAddToCart },
19
19
  * { id: "buy", label: t("buyNow"), variant: "primary", onClick: handleBuyNow },
20
20
  * ],
@@ -53,11 +53,6 @@ export interface BottomAction {
53
53
  disabled?: boolean;
54
54
  /** Show spinner inside the button. */
55
55
  loading?: boolean;
56
- /**
57
- * When true (default) the button grows to fill remaining space.
58
- * Set to false for compact icon-only buttons.
59
- */
60
- grow?: boolean;
61
56
  }
62
57
  export interface BottomBulkConfig {
63
58
  /** Number of currently selected items. Bar activates when > 0. */
@@ -16,7 +16,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
16
16
  * ```tsx
17
17
  * useBottomActions({
18
18
  * actions: [
19
- * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", grow: false, onClick: handleWishlist },
19
+ * { id: "wishlist", icon: <Heart className="w-4 h-4" />, label: t("wishlist"), variant: "ghost", onClick: handleWishlist },
20
20
  * { id: "cart", label: t("addToCart"), variant: "outline", onClick: handleAddToCart },
21
21
  * { id: "buy", label: t("buyNow"), variant: "primary", onClick: handleBuyNow },
22
22
  * ],
@@ -28,10 +28,10 @@ export function useBottomActions(options = {}) {
28
28
  const bulkRef = useRef(options.bulk);
29
29
  bulkRef.current = options.bulk;
30
30
  const actionKey = (options.actions ?? [])
31
- .map(({ id, label, variant, badge, disabled, loading, grow }) => `${id}|${label}|${variant}|${badge}|${disabled}|${loading}|${grow}`)
31
+ .map(({ id, label, variant, badge, disabled, loading }) => `${id}|${label}|${variant}|${badge}|${disabled}|${loading}`)
32
32
  .join(",");
33
33
  const bulkCountKey = options.bulk
34
- ? `${options.bulk.selectedCount}|${options.bulk.actions.map((a) => `${a.id}|${a.label}|${a.variant}|${a.badge}|${a.disabled}|${a.loading}|${a.grow}`).join(",")}`
34
+ ? `${options.bulk.selectedCount}|${options.bulk.actions.map((a) => `${a.id}|${a.label}|${a.variant}|${a.badge}|${a.disabled}|${a.loading}`).join(",")}`
35
35
  : "";
36
36
  useEffect(() => {
37
37
  if (!isTop)
@@ -1,4 +1,5 @@
1
1
  import { type FormShellContextValue } from "../../../ui/forms/FormShell";
2
+ import type { BottomAction } from "../BottomActionsContext";
2
3
  export interface UseFormBottomActionsOptions {
3
4
  /** Runs when the pinned primary button is pressed. */
4
5
  onSubmit: () => void | Promise<void>;
@@ -26,6 +27,16 @@ export interface UseFormBottomActionsOptions {
26
27
  onClick: () => void;
27
28
  disabled?: boolean;
28
29
  };
30
+ /**
31
+ * Further actions this form offers, spliced between the destructive one and
32
+ * Submit so the ordering rule survives: destructive furthest from the thumb,
33
+ * submit under it.
34
+ *
35
+ * Exists because the bar was hard-capped at three. `/admin/users` needs five
36
+ * (three forms plus soft-ban and hard-ban), so its extra actions had nowhere
37
+ * to go and stayed as an inline row the pinned bar then competed with.
38
+ */
39
+ extraActions?: BottomAction[];
29
40
  ctx?: FormShellContextValue;
30
41
  }
31
42
  /**
@@ -63,5 +74,5 @@ export interface UseFormBottomActionsOptions {
63
74
  * overlay tells us, because neither portal leaves a detectable trace in
64
75
  * the DOM at the form's position.
65
76
  */
66
- export declare function useFormBottomActions({ onSubmit, onCancel, submitLabel, cancelLabel, isLoading, disabled, enabled, destructiveAction, ctx: ctxProp, }: UseFormBottomActionsOptions): void;
77
+ export declare function useFormBottomActions({ onSubmit, onCancel, submitLabel, cancelLabel, isLoading, disabled, enabled, destructiveAction, extraActions, ctx: ctxProp, }: UseFormBottomActionsOptions): void;
67
78
  export default useFormBottomActions;
@@ -40,7 +40,7 @@ import { useBottomActions } from "./useBottomActions";
40
40
  * overlay tells us, because neither portal leaves a detectable trace in
41
41
  * the DOM at the form's position.
42
42
  */
43
- export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save", cancelLabel = "Cancel", isLoading = false, disabled = false, enabled = true, destructiveAction, ctx: ctxProp, }) {
43
+ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save", cancelLabel = "Cancel", isLoading = false, disabled = false, enabled = true, destructiveAction, extraActions, ctx: ctxProp, }) {
44
44
  const ambient = useContext(FormShellContext);
45
45
  const ctx = ctxProp ?? ambient;
46
46
  const insideOverlay = useIsInsideOverlay();
@@ -53,14 +53,18 @@ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save",
53
53
  const list = [];
54
54
  /*
55
55
  * Destructive FIRST, so it is furthest from the thumb's resting position
56
- * on the primary action. It never grows, so Save keeps the width.
56
+ * on the primary action, which BottomActions renders last.
57
+ *
58
+ * None of these set a width. `grow` no longer exists on BottomAction —
59
+ * ActionRow sizes every action from its own label and shares only the
60
+ * leftover, so "Cancel" and "Save" each get a width that fits their text
61
+ * instead of Cancel collapsing to a 44px square while Save took the rest.
57
62
  */
58
63
  if (destructiveAction) {
59
64
  list.push({
60
65
  id: "form-destructive",
61
66
  label: destructiveAction.label,
62
67
  variant: "danger",
63
- grow: false,
64
68
  onClick: destructiveAction.onClick,
65
69
  disabled: destructiveAction.disabled || isLoading,
66
70
  });
@@ -70,22 +74,24 @@ export function useFormBottomActions({ onSubmit, onCancel, submitLabel = "Save",
70
74
  id: "form-cancel",
71
75
  label: cancelLabel,
72
76
  variant: "outline",
73
- grow: false,
74
77
  onClick: onCancel,
75
78
  disabled: isLoading,
76
79
  });
77
80
  }
81
+ if (extraActions?.length)
82
+ list.push(...extraActions);
83
+ // Submit LAST: BottomActions treats the final action as the primary and
84
+ // gives it its own full-width line once the bar holds three or more.
78
85
  list.push({
79
86
  id: "form-submit",
80
87
  label: submitLabel,
81
88
  variant: "primary",
82
- grow: true,
83
89
  loading: isLoading,
84
90
  disabled: disabled || isLoading,
85
91
  onClick: () => void onSubmit(),
86
92
  });
87
93
  return list;
88
- }, [active, onCancel, cancelLabel, submitLabel, isLoading, disabled, onSubmit, destructiveAction]);
94
+ }, [active, onCancel, cancelLabel, submitLabel, isLoading, disabled, onSubmit, destructiveAction, extraActions]);
89
95
  useBottomActions({
90
96
  actions,
91
97
  // Encodes the count on purpose — see (1) in the header.
@@ -20,7 +20,16 @@ export declare const lotterySlotWriteSchema: z.ZodObject<{
20
20
  slotNumber: number;
21
21
  image?: string | undefined;
22
22
  }>;
23
+ /**
24
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
25
+ * by schema instance and every zod wrapper returns a new one.
26
+ */
23
27
  export declare const lotteryConfigWriteSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
28
+ pricingMode: z.ZodEnum<["uniform", "variable"]>;
29
+ uniformPrice: z.ZodOptional<z.ZodNumber>;
30
+ drawWindowDurationMinutes: z.ZodNumber;
31
+ maxPullsPerTransaction: z.ZodNumber;
32
+ maxPullsPerUser: z.ZodNumber;
24
33
  slots: z.ZodArray<z.ZodObject<{
25
34
  slotNumber: z.ZodNumber;
26
35
  name: z.ZodString;
@@ -37,11 +46,6 @@ export declare const lotteryConfigWriteSchema: z.ZodEffects<z.ZodEffects<z.ZodOb
37
46
  slotNumber: number;
38
47
  image?: string | undefined;
39
48
  }>, "many">;
40
- pricingMode: z.ZodEnum<["uniform", "variable"]>;
41
- uniformPrice: z.ZodOptional<z.ZodNumber>;
42
- drawWindowDurationMinutes: z.ZodNumber;
43
- maxPullsPerTransaction: z.ZodNumber;
44
- maxPullsPerUser: z.ZodNumber;
45
49
  }, "strict", z.ZodTypeAny, {
46
50
  slots: {
47
51
  name: string;