@mohasinac/appkit 4.30.19 → 4.31.1

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 +7 -2
  313. package/dist/next/index.js +36 -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,13 +1,29 @@
1
+ import { type NavSearchable } from "../../../shared/features/layout/matchesNavQuery";
1
2
  /**
2
- * Shared inline-filter logic for the Admin/Store/User sidebars — lowercase
3
- * substring match against item labels, collapsing to only groups with a
4
- * surviving match. Each sidebar keeps its own accordion/markup; only this
5
- * pure filtering algorithm is shared so it isn't rewritten three times.
3
+ * Shared inline-filter logic for the Admin/Store/User sidebars. Each sidebar
4
+ * keeps its own accordion/markup; only this filtering is shared so it is not
5
+ * rewritten three times.
6
+ *
7
+ * ## It searches what a screen is FOR, not just what it is called
8
+ *
9
+ * This was `item.label.toLowerCase().includes(q)`, which could only find a
10
+ * screen by the name already on the button. "refund" found nothing, because
11
+ * the screen is called Payouts. "postcode" found nothing, because the field is
12
+ * called PIN code. "maintenance" found nothing, because that toggle lives
13
+ * inside Site Settings. W6 gave every nav item a `description` and `keywords`;
14
+ * `matchesNavQuery` reads all three.
15
+ *
16
+ * ## Results are RANKED, and within a group only
17
+ *
18
+ * A label hit beats a keyword hit beats a description hit, so typing "orders"
19
+ * puts **Orders** above the guide that merely mentions orders. Groups keep
20
+ * their declared order — reordering them by best score would move the sidebar
21
+ * around under the reader's cursor while they type.
6
22
  */
7
23
  export declare function useSidebarSearch<Item extends {
8
24
  href: string;
9
25
  label: string;
10
- }, Group extends {
26
+ } & Partial<NavSearchable>, Group extends {
11
27
  title: string;
12
28
  items: Item[];
13
29
  defaultOpen?: boolean;
@@ -1,10 +1,26 @@
1
1
  "use client";
2
2
  import { useMemo, useState } from "react";
3
+ import { matchesNavQuery } from "../../../shared/features/layout/matchesNavQuery";
3
4
  /**
4
- * Shared inline-filter logic for the Admin/Store/User sidebars — lowercase
5
- * substring match against item labels, collapsing to only groups with a
6
- * surviving match. Each sidebar keeps its own accordion/markup; only this
7
- * pure filtering algorithm is shared so it isn't rewritten three times.
5
+ * Shared inline-filter logic for the Admin/Store/User sidebars. Each sidebar
6
+ * keeps its own accordion/markup; only this filtering is shared so it is not
7
+ * rewritten three times.
8
+ *
9
+ * ## It searches what a screen is FOR, not just what it is called
10
+ *
11
+ * This was `item.label.toLowerCase().includes(q)`, which could only find a
12
+ * screen by the name already on the button. "refund" found nothing, because
13
+ * the screen is called Payouts. "postcode" found nothing, because the field is
14
+ * called PIN code. "maintenance" found nothing, because that toggle lives
15
+ * inside Site Settings. W6 gave every nav item a `description` and `keywords`;
16
+ * `matchesNavQuery` reads all three.
17
+ *
18
+ * ## Results are RANKED, and within a group only
19
+ *
20
+ * A label hit beats a keyword hit beats a description hit, so typing "orders"
21
+ * puts **Orders** above the guide that merely mentions orders. Groups keep
22
+ * their declared order — reordering them by best score would move the sidebar
23
+ * around under the reader's cursor while they type.
8
24
  */
9
25
  export function useSidebarSearch(groups) {
10
26
  const [query, setQuery] = useState("");
@@ -12,9 +28,14 @@ export function useSidebarSearch(groups) {
12
28
  const filteredGroups = useMemo(() => {
13
29
  if (!isSearching)
14
30
  return groups;
15
- const q = query.trim().toLowerCase();
16
31
  return groups
17
- .map((group) => ({ ...group, items: group.items.filter((item) => item.label.toLowerCase().includes(q)) }))
32
+ .map((group) => {
33
+ const scored = group.items
34
+ .map((item) => ({ item, score: matchesNavQuery(item, query) }))
35
+ .filter((entry) => entry.score > 0)
36
+ .sort((a, b) => b.score - a.score);
37
+ return { ...group, items: scored.map((entry) => entry.item) };
38
+ })
18
39
  .filter((group) => group.items.length > 0);
19
40
  }, [groups, query, isSearching]);
20
41
  return { query, setQuery, isSearching, filteredGroups };
@@ -1,5 +1,28 @@
1
+ /**
2
+ * LotteryAdminEditView — the slot configuration for one lottery. Sectionised
3
+ * 2026-08-30.
4
+ *
5
+ * ## Two schemas became one
6
+ *
7
+ * It declared `lotteryConfigFormSchema` on `<Form>` and parsed
8
+ * `lotteryConfigWriteSchema` in the submit handler, so the form schema's
9
+ * duplicate-slot and free-lottery rules had never executed. The form schema is
10
+ * deleted; `lotteryConfigWriteSchema` carries the annotations now and is both
11
+ * what this form renders from and what the route parses.
12
+ *
13
+ * ## Title and description are gone, not moved
14
+ *
15
+ * They were editable here and reached nothing: `LotteryConfigClient` posts
16
+ * `data.lotteryConfig` to `PUT .../lottery-config` and ignores the rest, and
17
+ * that route accepts only the config. An event's title, dates, status and
18
+ * media are edited in the ordinary event editor — this page is the slots. Two
19
+ * inputs that saved silently into nowhere are worse than their absence, which
20
+ * is the same call made on `rememberMe`. The title still appears, as the
21
+ * heading, so it is obvious which lottery is open.
22
+ */
1
23
  import React from "react";
2
24
  import type { LotteryConfigWriteInput } from "../../../../features/lottery/schemas/config-write";
25
+ /** One editable row of the slot table. */
3
26
  interface LotterySlotRow {
4
27
  slotNumber: number;
5
28
  name: string;
@@ -22,9 +45,6 @@ interface LotterySlotRow {
22
45
  * fullness check reads.
23
46
  */
24
47
  interface LotteryEventFormData {
25
- title: string;
26
- description?: string;
27
- type: "lottery";
28
48
  lotteryConfig: LotteryConfigWriteInput;
29
49
  }
30
50
  interface LotteryAdminEditViewProps {
@@ -1,98 +1,121 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useTransition } from "react";
4
- import { normalizeError } from "../../../../errors/normalize";
5
- import { toUserMessage } from "../../../../errors/error-display-map";
6
- import { Stack, Row, Text, Heading, Section, Container, Button, DataTable, } from "../../../../ui";
7
- import { Form, FieldInput, FieldSelect, Input } from "../../../../ui";
8
- import { lotteryConfigFormSchema } from "../../../shared/features/lottery/config-form";
9
- import { lotteryConfigWriteSchema } from "../../../../features/lottery/schemas/config-write";
3
+ /**
4
+ * LotteryAdminEditView — the slot configuration for one lottery. Sectionised
5
+ * 2026-08-30.
6
+ *
7
+ * ## Two schemas became one
8
+ *
9
+ * It declared `lotteryConfigFormSchema` on `<Form>` and parsed
10
+ * `lotteryConfigWriteSchema` in the submit handler, so the form schema's
11
+ * duplicate-slot and free-lottery rules had never executed. The form schema is
12
+ * deleted; `lotteryConfigWriteSchema` carries the annotations now and is both
13
+ * what this form renders from and what the route parses.
14
+ *
15
+ * ## Title and description are gone, not moved
16
+ *
17
+ * They were editable here and reached nothing: `LotteryConfigClient` posts
18
+ * `data.lotteryConfig` to `PUT .../lottery-config` and ignores the rest, and
19
+ * that route accepts only the config. An event's title, dates, status and
20
+ * media are edited in the ordinary event editor — this page is the slots. Two
21
+ * inputs that saved silently into nowhere are worse than their absence, which
22
+ * is the same call made on `rememberMe`. The title still appears, as the
23
+ * heading, so it is obvious which lottery is open.
24
+ */
25
+ import React from "react";
26
+ import { Button, DataTable, Input, Row, Stack, Text, } from "../../../../ui";
10
27
  import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
28
+ import { FormShellContext, useFormShellState } from "../../../../ui/forms/FormShell";
29
+ import { applyZodIssues } from "../../../../ui/forms/apply-zod-issues";
30
+ import { StackedViewShell } from "../../../../ui";
31
+ import { buildSectionsFromSchema, visibleValues } from "../../../../features/shell/build-sections";
32
+ import { SectionForm, useSectionFormNav } from "../../../../features/shell/SectionForm";
33
+ import { lotteryConfigWriteSchema } from "../../../../features/lottery/schemas/config-write";
34
+ const MAX_SLOTS = 200;
11
35
  export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
12
- const [isPending, startTransition] = useTransition();
13
- const [error, setError] = useState(null);
14
- const [title, setTitle] = useState(initialData?.title ?? "");
15
- const [description, setDescription] = useState(initialData?.description ?? "");
16
- const [pricingMode, setPricingMode] = useState(initialData?.pricingMode ?? "uniform");
17
- const [uniformPrice, setUniformPrice] = useState(String(initialData?.uniformPrice ?? 0));
18
- const [drawWindowMinutes, setDrawWindowMinutes] = useState(String(initialData?.drawWindowDurationMinutes ?? 5));
19
- const [maxPullsPerTx, setMaxPullsPerTx] = useState(String(initialData?.maxPullsPerTransaction ?? 1));
20
- const [maxPullsPerUser, setMaxPullsPerUser] = useState(String(initialData?.maxPullsPerUser ?? 1));
21
- const [slots, setSlots] = useState(initialData?.slots ?? [{ slotNumber: 1, name: "", price: 0 }]);
22
- const addSlot = () => {
23
- setSlots((prev) => [
24
- ...prev,
25
- { slotNumber: prev.length + 1, name: "", price: 0 },
26
- ]);
27
- };
28
- const removeSlot = (idx) => {
29
- setSlots((prev) => prev.filter((_, i) => i !== idx).map((s, i) => ({ ...s, slotNumber: i + 1 })));
30
- };
31
- const updateSlot = (idx, field, value) => {
32
- setSlots((prev) => prev.map((s, i) => i === idx
33
- ? { ...s, [field]: field === "price" ? Math.round(parseFloat(value) * 100) / 100 || 0 : value }
34
- : s));
35
- };
36
+ const [isPending, startTransition] = React.useTransition();
37
+ const [form, setForm] = React.useState({
38
+ pricingMode: initialData?.pricingMode ?? "uniform",
39
+ uniformPrice: initialData?.uniformPrice ?? 0,
40
+ drawWindowDurationMinutes: initialData?.drawWindowDurationMinutes ?? 5,
41
+ maxPullsPerTransaction: initialData?.maxPullsPerTransaction ?? 1,
42
+ maxPullsPerUser: initialData?.maxPullsPerUser ?? 1,
43
+ slots: initialData?.slots ?? [{ slotNumber: 1, name: "", price: 0 }],
44
+ });
45
+ const patch = (partial) => setForm((prev) => Object.assign({}, prev, partial));
46
+ const sections = React.useMemo(() => buildSectionsFromSchema(lotteryConfigWriteSchema, {
47
+ renderers: {
48
+ slots: ({ values, onChange }) => (_jsx(SlotTable, { slots: values.slots, showPrice: values.pricingMode === "variable", onSlotsChange: (next) => onChange({ slots: next }) })),
49
+ },
50
+ }), []);
51
+ const nav = useSectionFormNav(sections, form, { scope: "admin:lottery-config" });
52
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(lotteryConfigWriteSchema, {
53
+ sections: nav.sectionMeta,
54
+ onGoToSection: nav.goToSection,
55
+ fieldToSectionIndex: nav.fieldToSectionIndex,
56
+ });
36
57
  const handleSubmit = () => {
37
- setError(null);
58
+ clearErrors();
59
+ /*
60
+ * Per-slot prices are dropped under uniform pricing, where the price of a
61
+ * slot IS `uniformPrice`. They used to be sent in both modes, so a figure
62
+ * loaded from `initialData` survived a switch to uniform — unreachable on
63
+ * screen, still written on save. `visibleValues` cannot reach inside an
64
+ * array, so this one masking stays explicit.
65
+ */
66
+ const draft = visibleValues(lotteryConfigWriteSchema, form);
67
+ const candidate = {
68
+ ...draft,
69
+ slots: form.slots.map((s) => ({
70
+ slotNumber: s.slotNumber,
71
+ name: s.name,
72
+ image: s.image?.trim() || undefined,
73
+ price: form.pricingMode === "variable" ? s.price : 0,
74
+ })),
75
+ };
76
+ const parsed = lotteryConfigWriteSchema.safeParse(candidate);
77
+ if (!parsed.success) {
78
+ applyZodIssues(parsed.error.issues, setFieldError);
79
+ return;
80
+ }
38
81
  startTransition(async () => {
39
- try {
40
- /*
41
- * 🛑 This used to send `isBooked: false` and `weight: 0` on every slot,
42
- * and drop `bookedByUserId` / `bookedByDisplayName` /
43
- * `bookedByUserLotteryNumber` entirely — against a `.passthrough()`
44
- * PATCH, which made the first save of a live lottery erase every
45
- * purchased slot, silently, with a 200.
46
- *
47
- * The write shape now has NO booking fields at all and is `.strict()`,
48
- * so a slot's buyer is not something this form can express, let alone
49
- * overwrite. `PUT /lottery-config` merges by `slotNumber` against the
50
- * stored config and is the only writer.
51
- */
52
- const candidate = {
53
- slots: slots.map((s) => ({
54
- slotNumber: s.slotNumber,
55
- name: s.name,
56
- image: s.image?.trim() || undefined,
57
- price: s.price,
58
- })),
59
- pricingMode,
60
- uniformPrice: pricingMode === "uniform"
61
- ? Math.round(parseFloat(uniformPrice) * 100) / 100 || 0
62
- : undefined,
63
- drawWindowDurationMinutes: parseInt(drawWindowMinutes, 10) || 5,
64
- maxPullsPerTransaction: parseInt(maxPullsPerTx, 10) || 1,
65
- maxPullsPerUser: parseInt(maxPullsPerUser, 10) || 1,
66
- };
67
- /*
68
- * The schema was passed to `<Form>` and never actually run — so the
69
- * `parseFloat(x) || 0` coercions above, which it exists to catch, were
70
- * still live. Parsed here so a bad slot is refused on the client
71
- * rather than discovered as a 400.
72
- */
73
- const parsed = lotteryConfigWriteSchema.safeParse(candidate);
74
- if (!parsed.success) {
75
- setError(parsed.error.issues[0]?.message ?? "Check the lottery settings.");
76
- return;
77
- }
78
- await onSubmit({ title, description, type: "lottery", lotteryConfig: parsed.data });
79
- }
80
- catch (err) {
81
- const e = normalizeError(err);
82
- setError(toUserMessage(e.code, undefined, { fallback: "Failed to save lottery." }));
83
- }
82
+ await onSubmit({ lotteryConfig: parsed.data });
84
83
  });
85
84
  };
86
- // Slot table columns
87
- const slotColumns = [
85
+ return (_jsx(StackedViewShell, { portal: "admin", title: eventId
86
+ ? `Edit lottery${initialData?.title ? ` — ${initialData.title}` : ""}`
87
+ : "Create lottery", sections: [
88
+ _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: patch, onSubmit: handleSubmit, schema: lotteryConfigWriteSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isPending, submitLabel: eventId ? "Update lottery" : "Create lottery" })] }, "lottery-config-form"),
89
+ ] }));
90
+ }
91
+ /**
92
+ * The repeatable slot rows.
93
+ *
94
+ * A module-level component rather than an inline renderer: its handlers would
95
+ * otherwise sit six braces deep inside `useMemo`, which is what
96
+ * `audit-code-quality`'s DEEP_NESTING rule exists to stop.
97
+ */
98
+ function SlotTable({ slots, showPrice, onSlotsChange, }) {
99
+ const addSlot = () => onSlotsChange([...slots, { slotNumber: slots.length + 1, name: "", price: 0 }]);
100
+ const removeSlot = (idx) => onSlotsChange(slots.filter((_, i) => i !== idx).map((s, i) => ({ ...s, slotNumber: i + 1 })));
101
+ const updateSlot = (idx, field, value) => onSlotsChange(slots.map((s, i) => i === idx
102
+ ? {
103
+ ...s,
104
+ [field]: field === "price"
105
+ ? Math.round(parseFloat(value) * 100) / 100 || 0
106
+ : value,
107
+ }
108
+ : s));
109
+ const rows = slots.map((s, i) => ({ ...s, _idx: i }));
110
+ const columns = [
88
111
  {
89
112
  key: "slotNumber",
90
113
  header: "#",
91
- render: (s) => _jsx(Text, { size: "xs", color: "muted", family: "mono", children: s.slotNumber }),
114
+ render: (s) => (_jsx(Text, { size: "xs", color: "muted", family: "mono", children: s.slotNumber })),
92
115
  },
93
116
  {
94
117
  key: "name",
95
- header: "Prize Name",
118
+ header: "Prize name",
96
119
  render: (s) => (_jsx(Input, { type: "text", className: "w-full", placeholder: `Prize for slot ${s.slotNumber}`, value: s.name, onChange: (e) => updateSlot(s._idx, "name", e.target.value), "aria-label": `Slot ${s.slotNumber} prize name` })),
97
120
  },
98
121
  {
@@ -100,7 +123,7 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
100
123
  header: "Image",
101
124
  render: (s) => (_jsx(Input, { type: "text", className: "w-full", placeholder: "/media/\u2026 or https://\u2026", value: s.image ?? "", onChange: (e) => updateSlot(s._idx, "image", e.target.value), "aria-label": `Slot ${s.slotNumber} prize image URL` })),
102
125
  },
103
- ...(pricingMode === "variable"
126
+ ...(showPrice
104
127
  ? [
105
128
  {
106
129
  key: "price",
@@ -110,9 +133,5 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
110
133
  ]
111
134
  : []),
112
135
  ];
113
- const slotsWithIdx = slots.map((s, i) => ({ ...s, _idx: i }));
114
- return (_jsx(Container, { children: _jsx(Section, { children: _jsx(Form, { schema: lotteryConfigFormSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-6", children: ({ clearErrors }) => (_jsxs(Stack, { gap: "xl", children: [_jsx(FormErrorSummary, {}), _jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: eventId ? "Edit Lottery" : "Create Lottery" }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Basic Info" }), _jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: (v) => { setTitle(v); clearErrors(); } }), _jsx(FieldInput, { name: "description", label: "Description", value: description, onChange: (v) => { setDescription(v); clearErrors(); } })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Configuration" }), _jsx(FieldSelect, { name: "pricingMode", label: "Pricing Mode", value: pricingMode, options: [
115
- { label: "Uniform (all slots same price)", value: "uniform" },
116
- { label: "Variable (per-slot pricing)", value: "variable" },
117
- ], onChange: (v) => setPricingMode(v) }), pricingMode === "uniform" && (_jsx(FieldInput, { name: "uniformPrice", label: "Price per Slot (\u20B9)", type: "number", value: uniformPrice, onChange: (v) => setUniformPrice(v) })), _jsx(FieldInput, { name: "drawWindowMinutes", label: "Draw Window (minutes)", type: "number", value: drawWindowMinutes, onChange: (v) => setDrawWindowMinutes(v) }), _jsx(FieldInput, { name: "maxPullsPerTx", label: "Max Pulls per Transaction", type: "number", value: maxPullsPerTx, onChange: (v) => setMaxPullsPerTx(v) }), _jsx(FieldInput, { name: "maxPullsPerUser", label: "Max Pulls per User", type: "number", value: maxPullsPerUser, onChange: (v) => setMaxPullsPerUser(v) })] }), _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Heading, { level: 2, size: "lg", weight: "semibold", children: ["Slots (", slots.length, ")"] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: addSlot, disabled: slots.length >= 200, children: "+ Add Slot" })] }), _jsx(DataTable, { data: slotsWithIdx, columns: slotColumns, keyExtractor: (s) => String(s.slotNumber), emptyMessage: "No slots yet.", actions: (s) => slots.length > 1 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeSlot(s._idx), "aria-label": `Remove slot ${s.slotNumber}`, children: "\u00D7" })) : null })] }), error && _jsx(Text, { className: "text-error", size: "sm", role: "alert", children: error }), _jsx(Button, { type: "submit", variant: "primary", isLoading: isPending, onClick: handleSubmit, className: "w-full sm:w-auto", children: isPending ? "Saving…" : eventId ? "Update Lottery" : "Create Lottery" })] })) }) }) }));
136
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { size: "sm", color: "muted", children: [slots.length, " slot", slots.length === 1 ? "" : "s"] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: addSlot, disabled: slots.length >= MAX_SLOTS, children: "+ Add slot" })] }), _jsx(DataTable, { data: rows, columns: columns, keyExtractor: (s) => String(s.slotNumber), emptyMessage: "No slots yet.", actions: (s) => slots.length > 1 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeSlot(s._idx), "aria-label": `Remove slot ${s.slotNumber}`, children: "\u00D7" })) : null })] }));
118
137
  }
@@ -1,3 +1,19 @@
1
+ /**
2
+ * User-facing lottery pull form — claim a slot with a UPI reference.
3
+ *
4
+ * Fields come from `lotteryPullSchema`. Two need a `renderers` entry because
5
+ * their labels depend on `totalSlots`, which a static annotation cannot know,
6
+ * and one needs `datetime-local` rather than a plain date.
7
+ *
8
+ * ## The upper bound stays a runtime check, on purpose
9
+ *
10
+ * `purchasedItemNumber` is `.min(1)` in the schema and has no `.max()`: the
11
+ * ceiling is `totalSlots`, a prop that varies per lottery. A schema shared with
12
+ * the route cannot carry it, so it is checked after the parse rather than
13
+ * pretended into the annotation. Everything else the hand-written validation
14
+ * used to duplicate — the reference length, the phone shape, the date — now
15
+ * comes from the schema, which is where those rules already lived.
16
+ */
1
17
  import React from "react";
2
18
  interface LotteryPullFormProps {
3
19
  sourceType: "event" | "product";
@@ -11,10 +27,5 @@ interface LotteryPullFormProps {
11
27
  slotName: string;
12
28
  }) => void;
13
29
  }
14
- /**
15
- * User-facing lottery pull form.
16
- * Submits TX ID + phone + payment time + item number.
17
- * On success, shows the user their lottery number and assigned prize slot.
18
- */
19
30
  export declare function LotteryPullForm({ sourceType, eventId, productId, totalSlots, maxPullsPerUser, onSuccess, }: LotteryPullFormProps): React.JSX.Element;
20
31
  export {};
@@ -1,34 +1,67 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { Stack, Row, Text, Heading, Span, } from "../../../../ui";
5
- import { Form, FieldInput, Button } from "../../../../ui";
3
+ /**
4
+ * User-facing lottery pull form — claim a slot with a UPI reference.
5
+ *
6
+ * Fields come from `lotteryPullSchema`. Two need a `renderers` entry because
7
+ * their labels depend on `totalSlots`, which a static annotation cannot know,
8
+ * and one needs `datetime-local` rather than a plain date.
9
+ *
10
+ * ## The upper bound stays a runtime check, on purpose
11
+ *
12
+ * `purchasedItemNumber` is `.min(1)` in the schema and has no `.max()`: the
13
+ * ceiling is `totalSlots`, a prop that varies per lottery. A schema shared with
14
+ * the route cannot carry it, so it is checked after the parse rather than
15
+ * pretended into the annotation. Everything else the hand-written validation
16
+ * used to duplicate — the reference length, the phone shape, the date — now
17
+ * comes from the schema, which is where those rules already lived.
18
+ */
19
+ import { useMemo, useState } from "react";
6
20
  import { useApiMutation } from "../../../../client/api/useApiMutation";
7
- import { apiClient } from "../../../../http";
8
21
  import { lotteryPullSchema } from "../../../../features/admin/schemas/admin-user-form";
22
+ import { buildSectionsFromSchema } from "../../../../features/shell/build-sections";
23
+ import { SectionForm, useSectionFormNav } from "../../../../features/shell/SectionForm";
24
+ import { apiClient } from "../../../../http";
25
+ import { FieldInput, Heading, Row, Span, Stack, Text } from "../../../../ui";
26
+ import { applyZodIssues } from "../../../../ui/forms/apply-zod-issues";
9
27
  import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
10
- /**
11
- * User-facing lottery pull form.
12
- * Submits TX ID + phone + payment time + item number.
13
- * On success, shows the user their lottery number and assigned prize slot.
14
- */
28
+ import { FormShellContext, useFormShellState } from "../../../../ui/forms/FormShell";
15
29
  export function LotteryPullForm({ sourceType, eventId, productId, totalSlots, maxPullsPerUser, onSuccess, }) {
16
30
  const [result, setResult] = useState(null);
17
- const [transactionId, setTransactionId] = useState("");
18
- const [paymentTime, setPaymentTime] = useState("");
19
- const [itemNumber, setItemNumber] = useState("");
20
- const [userPhone, setUserPhone] = useState("");
31
+ const [form, setForm] = useState({
32
+ sourceType,
33
+ transactionId: "",
34
+ paymentTime: "",
35
+ purchasedItemNumber: "",
36
+ userPhone: "",
37
+ });
21
38
  const apiRoute = sourceType === "event"
22
39
  ? `/api/events/${eventId ?? ""}/lottery-pull`
23
40
  : `/api/products/${productId ?? ""}/lottery-pull`;
41
+ const sections = useMemo(() => buildSectionsFromSchema(lotteryPullSchema, {
42
+ renderers: {
43
+ paymentTime: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "paymentTime", label: "Payment Date & Time",
44
+ // `datetime-local`, not `date` — the pull is matched against a
45
+ // bank timestamp, so the time of day is part of the evidence.
46
+ type: "datetime-local", required: true, value: values.paymentTime, error: errors.paymentTime, onChange: (v) => onChange({ paymentTime: v }) })),
47
+ purchasedItemNumber: ({ values, onChange, errors }) => (_jsx(FieldInput, { name: "purchasedItemNumber", label: `Slot Number (1 – ${totalSlots})`, type: "number", placeholder: `Enter a number between 1 and ${totalSlots}`, required: true, value: values.purchasedItemNumber, error: errors.purchasedItemNumber, onChange: (v) => onChange({ purchasedItemNumber: v }) })),
48
+ },
49
+ }), [totalSlots]);
50
+ const nav = useSectionFormNav(sections, form, { scope: "lottery:pull" });
51
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(lotteryPullSchema, {
52
+ sections: nav.sectionMeta,
53
+ onGoToSection: nav.goToSection,
54
+ fieldToSectionIndex: nav.fieldToSectionIndex,
55
+ });
24
56
  const mutation = useApiMutation({
57
+ errorMessage: "Could not submit your entry.",
25
58
  mutationFn: () => apiClient.post(apiRoute, {
26
59
  sourceType,
27
60
  ...(sourceType === "event" ? { eventId } : { productId }),
28
- transactionId,
29
- paymentTime: new Date(paymentTime).toISOString(),
30
- purchasedItemNumber: parseInt(itemNumber, 10),
31
- userPhone,
61
+ transactionId: form.transactionId,
62
+ paymentTime: new Date(form.paymentTime).toISOString(),
63
+ purchasedItemNumber: parseInt(form.purchasedItemNumber, 10),
64
+ userPhone: form.userPhone,
32
65
  }),
33
66
  successMessage: "Your lottery entry has been submitted!",
34
67
  onSuccess: (data) => {
@@ -38,30 +71,22 @@ export function LotteryPullForm({ sourceType, eventId, productId, totalSlots, ma
38
71
  }
39
72
  },
40
73
  });
74
+ const onSubmit = () => {
75
+ clearErrors();
76
+ const parsed = lotteryPullSchema.safeParse(form);
77
+ if (!parsed.success) {
78
+ applyZodIssues(parsed.error.issues, setFieldError);
79
+ return;
80
+ }
81
+ // Data-dependent ceiling — see the header.
82
+ if (parsed.data.purchasedItemNumber > totalSlots) {
83
+ setFieldError("purchasedItemNumber", `This lottery has ${totalSlots} slots.`);
84
+ return;
85
+ }
86
+ mutation.mutate();
87
+ };
41
88
  if (result) {
42
89
  return (_jsxs(Stack, { gap: "sm", padding: "md", rounded: "2xl", surface: "success-surface", className: "border border-success/20", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { layout: "inline-flex", color: "inverse", weight: "semibold", className: "bg-success text-[11px] tracking-wide", padding: "pill-sm", rounded: "full", transform: "uppercase", children: "Entry Confirmed" }), _jsxs(Heading, { level: 2, className: "text-success", size: "lg", weight: "bold", children: ["You're #", result.userLotteryNumber, "!"] })] }), _jsxs(Text, { className: "text-success", size: "sm", children: ["You've been assigned", " ", _jsxs(Span, { weight: "bold", children: ["Slot #", result.assignedPrizeSlotNumber] }), result.slotName ? ` — ${result.slotName}` : "", ". Good luck!"] })] }));
43
90
  }
44
- return (_jsx(Form, { schema: lotteryPullSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-4", children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FormErrorSummary, {}), _jsx(Heading, { level: 3, size: "lg", weight: "semibold", children: "Submit Your Entry" }), _jsxs(Text, { size: "sm", color: "muted", children: ["Enter your UPI Transaction ID and payment details to claim your slot.", " ", "You may enter up to ", maxPullsPerUser, " time", maxPullsPerUser === 1 ? "" : "s", "."] }), _jsx(FieldInput, { name: "transactionId", label: "Transaction ID (UPI Ref / Payment Ref)", placeholder: "e.g. 123456789012", required: true, value: transactionId, onChange: (v) => { setTransactionId(v); clearErrors(); } }), _jsx(FieldInput, { name: "paymentTime", label: "Payment Date & Time", type: "datetime-local", required: true, value: paymentTime, onChange: (v) => { setPaymentTime(v); clearErrors(); } }), _jsx(FieldInput, { name: "itemNumber", label: `Slot Number (1 – ${totalSlots})`, type: "number", placeholder: `Enter a number between 1 and ${totalSlots}`, required: true, value: itemNumber, onChange: (v) => { setItemNumber(v); clearErrors(); } }), _jsx(FieldInput, { name: "userPhone", label: "Your Phone Number", type: "tel", placeholder: "e.g. 9876543210", required: true, value: userPhone, onChange: (v) => { setUserPhone(v); clearErrors(); } }), _jsx(Button, { type: "submit", variant: "primary", isLoading: mutation.isPending, onClick: () => {
45
- clearErrors();
46
- const num = parseInt(itemNumber, 10);
47
- let valid = true;
48
- if (!transactionId || transactionId.length < 4) {
49
- setFieldError("transactionId", "Transaction ID must be at least 4 characters");
50
- valid = false;
51
- }
52
- if (!paymentTime) {
53
- setFieldError("paymentTime", "Payment date & time is required");
54
- valid = false;
55
- }
56
- if (isNaN(num) || num < 1 || num > totalSlots) {
57
- setFieldError("itemNumber", `Slot number must be between 1 and ${totalSlots}`);
58
- valid = false;
59
- }
60
- if (!userPhone || userPhone.replace(/\D/g, "").length < 10) {
61
- setFieldError("userPhone", "Enter a valid phone number (minimum 10 digits)");
62
- valid = false;
63
- }
64
- if (valid)
65
- mutation.mutate();
66
- }, className: "w-full", children: mutation.isPending ? "Submitting…" : "Submit Entry" })] })) }));
91
+ return (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, size: "lg", weight: "semibold", children: "Submit Your Entry" }), _jsxs(Text, { size: "sm", color: "muted", children: ["Enter your UPI Transaction ID and payment details to claim your slot. You may enter up to", " ", maxPullsPerUser, " time", maxPullsPerUser === 1 ? "" : "s", "."] }), _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: (partial) => setForm((prev) => Object.assign({}, prev, partial)), onSubmit: onSubmit, schema: lotteryPullSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: mutation.isPending, submitLabel: "Submit Entry" })] })] }));
67
92
  }
@@ -26,6 +26,8 @@ export declare function checkAnyPermission(resolved: ResolvedPermissions, requir
26
26
  type GetUser = () => Promise<{
27
27
  uid: string;
28
28
  role: string;
29
+ permissions?: readonly string[] | null;
30
+ /** @deprecated Legacy tester flags; still read during the migration window. */
29
31
  isTester?: boolean;
30
32
  canTestAdmin?: boolean;
31
33
  } | null>;
@@ -60,8 +60,9 @@ export function makeAdminSectionLayout(permission, opts) {
60
60
  redirect(opts.loginPath ?? String(ROUTES.AUTH.LOGIN));
61
61
  return null;
62
62
  }
63
- // admin role, or a tester explicitly flagged to test admin areas
64
- // (isTester && canTestAdmin), passes without a permission check
63
+ // Admin role, or a tester cleared for admin surfaces (the `tester` role
64
+ // plus `tester:admin-surfaces`, or the legacy isTester/canTestAdmin pair),
65
+ // passes without a permission check.
65
66
  if (isEffectiveAdminUser(user))
66
67
  return children;
67
68
  // non-employee non-admin roles have no business in /admin
@@ -5,10 +5,21 @@ import { safeRead } from "../../../../errors/safe-read";
5
5
  import { CATEGORIES_FEATURED_LIMIT, CATEGORIES_MENU_LIMIT, CATEGORIES_SITEMAP_LIMIT } from "../../../shared/features/categories/config";
6
6
  /** Full category document by slug — deduped per request via React.cache(). */
7
7
  export const getCategoryForDetail = cache(async (slug) => {
8
- const category = (await categoriesRepository.getCategoryBySlug(slug)) ?? null;
9
- if (category)
10
- void categoriesRepository.incrementViewCount(category.id);
11
- return category;
8
+ /*
9
+ * 🛑 No view-count write here — deleted 2026-08-31.
10
+ *
11
+ * This fired `incrementViewCount` on every render, which was two defects at
12
+ * once. It is a Firestore WRITE on the render path (Rule #6), and product
13
+ * and category detail were being counted TWICE by two systems that key
14
+ * differently: this one on the document by id, and the client
15
+ * `PageViewTracker` into `pageViews` by slug. They can never reconcile —
16
+ * they disagree on bots, on prefetch, and on JS-disabled clients — so the
17
+ * two numbers were guaranteed to drift and neither could be trusted.
18
+ *
19
+ * `pageViews` is the single counter now. `viewCount` on the document stays
20
+ * for historical rows; nothing increments it.
21
+ */
22
+ return (await categoriesRepository.getCategoryBySlug(slug)) ?? null;
12
23
  });
13
24
  /** Flat list of all categories at tier 1 (roots) — for nav/sitemap. */
14
25
  export const listRootCategories = cache(async () => {
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
- export * from "./actions";
3
2
  export * from "./adapters";
4
3
  export * from "./metadata";
5
4
  export * from "./og";
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
- export * from "./actions";
3
2
  export * from "./adapters";
4
3
  export * from "./metadata";
5
4
  export * from "./og";
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
- export * from "./actions";
4
3
  export * from "./metadata";
5
4
  export * from "./og";
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
- export * from "./actions";
4
3
  export * from "./metadata";
5
4
  export * from "./og";
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
- export * from "./actions";
4
3
  export * from "./metadata";
5
4
  export * from "./og";
@@ -1,5 +1,4 @@
1
1
  export * from "./data";
2
2
  export * from "./adapters";
3
- export * from "./actions";
4
3
  export * from "./metadata";
5
4
  export * from "./og";