@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
@@ -281,7 +281,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
281
281
  canFilter: boolean;
282
282
  canSort: boolean;
283
283
  };
284
- "classified.meetupArea": {
284
+ "classified.meetupArea.city": {
285
285
  canFilter: boolean;
286
286
  canSort: boolean;
287
287
  };
@@ -317,6 +317,10 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
317
317
  canFilter: boolean;
318
318
  canSort: boolean;
319
319
  };
320
+ "liveItem.transport.method": {
321
+ canFilter: boolean;
322
+ canSort: boolean;
323
+ };
320
324
  };
321
325
  /**
322
326
  * Virtual filter aliases — callers can pass `f=listingType==auction` for
@@ -594,7 +594,22 @@ ProductRepository.SIEVE_FIELDS = {
594
594
  // silently skipped. The toggle did nothing at all until 2026-08-21.
595
595
  shippingPaidBy: { canFilter: true, canSort: false },
596
596
  searchTxt: { canFilter: true, canSort: false },
597
- "classified.meetupArea": { canFilter: true, canSort: false },
597
+ /*
598
+ * `classified.meetupArea.city`, NOT `classified.meetupArea`.
599
+ *
600
+ * `meetupArea` is an OBJECT (`{city, locality, pincode}`), so an equality
601
+ * on the parent could never match anything a user typed. The city facet
602
+ * emits `classified.meetupArea.city` (TYPE_FACET_FIELD), `findField` is an
603
+ * exact-key lookup, and `throwExceptions:false` drops what it cannot
604
+ * resolve — so the /classified city filter rendered, counted toward the
605
+ * badge, and changed nothing. Both halves of Root Cause #62 in one entry:
606
+ * an orphan key that matches no field, beside a real field with no entry.
607
+ *
608
+ * The schema's own docstring already named the right path ("drives the
609
+ * (listingType, classified.meetupArea.city, createdAt) index"), and the
610
+ * store-scoped index has always been on `.city`.
611
+ */
612
+ "classified.meetupArea.city": { canFilter: true, canSort: false },
598
613
  "classified.acceptsShipping": { canFilter: true, canSort: false },
599
614
  "classified.negotiable": { canFilter: true, canSort: false },
600
615
  "digitalCode.codeDeliveryMethod": { canFilter: true, canSort: false },
@@ -603,6 +618,13 @@ ProductRepository.SIEVE_FIELDS = {
603
618
  "liveItem.sex": { canFilter: true, canSort: false },
604
619
  "liveItem.jurisdictionAllowed": { canFilter: true, canSort: false },
605
620
  "liveItem.cites": { canFilter: true, canSort: false },
621
+ /*
622
+ * The live-items transport facet. `StoreLiveItemsListing` has listed
623
+ * `liveTransportMethod` in its FILTER_KEYS — so ticking it inflated the
624
+ * active-filter badge — while the key had no TYPE_FACET_FIELD mapping and
625
+ * this field had no entry, so it never reached Firestore from either end.
626
+ */
627
+ "liveItem.transport.method": { canFilter: true, canSort: false },
606
628
  };
607
629
  /**
608
630
  * Virtual filter aliases — callers can pass `f=listingType==auction` for
@@ -64,11 +64,15 @@ export interface ProductClassifiedMeta {
64
64
  locality?: string;
65
65
  pincode?: string;
66
66
  };
67
- /** How the buyer reaches the seller (chat | phone | both). */
68
- contactMethod?: "chat" | "phone" | "both";
69
67
  /** Seller offers shipping in addition to meetup. */
70
68
  acceptsShipping?: boolean;
71
- /** Price is negotiable — UI hints "₹X (negotiable)". */
69
+ /**
70
+ * Price is negotiable — UI hints "₹X (negotiable)".
71
+ *
72
+ * Its sibling `allowOffers` is what actually gates the offer FLOOR on this
73
+ * type: unset means the buyer may still request to buy, but only at the
74
+ * asking price.
75
+ */
72
76
  negotiable?: boolean;
73
77
  }
74
78
  export interface ProductDigitalCodeMeta {
@@ -110,6 +110,9 @@ export const productFeatureFormSchema = z.object({
110
110
  storeId: annotate(productFeatureAdminCreateSchema.shape.storeId, {
111
111
  section: "scope", order: 4, row: "pair",
112
112
  help: "Only used when scope is \"store\".",
113
+ // Hidden for a global feature, and paired with visibleValues at the
114
+ // payload so a store id chosen and then abandoned is not saved.
115
+ when: (v) => v.scope === "store",
113
116
  }),
114
117
  isActive: annotate(productFeatureAdminCreateSchema.shape.isActive, {
115
118
  section: "visibility", sectionLabel: "Visibility", order: 1, row: "quarter",
@@ -5,7 +5,6 @@ import { useState } from "react";
5
5
  import { Pencil, Trash2, ToggleLeft, ToggleRight } from "lucide-react";
6
6
  import { Badge, BaseListingCard, Button, ConfirmDeleteModal, Div, IconButton, Row, Span, Stack, Text } from "../../../ui";
7
7
  import { useLongPress } from "../../../react/hooks/useLongPress";
8
- import { useFeatureFlags } from "../../../client/api/useFeatureFlags";
9
8
  import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
10
9
  const CLS_TOGGLE_ON = "h-4 w-4 text-success dark:text-success";
11
10
  const CLS_DELETE_BTN = "p-[var(--appkit-space-1-5)] text-[var(--appkit-color-text-muted)] hover:bg-error-surface dark:hover:bg-error-surface hover:text-error dark:hover:text-error transition-colors disabled:opacity-50";
@@ -102,12 +101,14 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
102
101
  const discountLabel = formatDiscount(n, labels);
103
102
  const hasAdminActions = Boolean(onEdit || onToggleActive || onDelete);
104
103
  // Show Claim by default on public surfaces; hide automatically when this
105
- // card is being rendered with admin/CRUD actions wired. Also hidden
106
- // whenever the COUPONS feature is disabled — the claim endpoint 404s in
107
- // that state, and previously that 404 was silently swallowed (see
108
- // handleClaim below) instead of the CTA simply not being offered.
109
- const { flags: featureFlags } = useFeatureFlags();
110
- const showClaim = (hideClaim === undefined ? !hasAdminActions : !hideClaim) && featureFlags.COUPONS === true;
104
+ // card is being rendered with admin/CRUD actions wired.
105
+ //
106
+ // This also consulted the `COUPONS` env feature flag, because the claim
107
+ // endpoint 404'd when it was off. Both flag systems were deleted 2026-08-29 —
108
+ // the flag was `true` in every environment, and the endpoint now answers
109
+ // unconditionally under its own auth — so the fetch that backed this check
110
+ // is gone too, along with a network request on every coupon card render.
111
+ const showClaim = hideClaim === undefined ? !hasAdminActions : !hideClaim;
111
112
  const handleCopy = () => {
112
113
  if (!n.code)
113
114
  return;
@@ -138,6 +138,20 @@ export interface ScammerDocument extends BaseDocument {
138
138
  * Admin records keep the uid; public page shows "Anonymous".
139
139
  */
140
140
  reportedByAnon: boolean;
141
+ /**
142
+ * The reporter ticked "I confirm this report is truthful and accurate".
143
+ *
144
+ * 🛑 The form has always REQUIRED this — a `z.literal(true)` in
145
+ * `scamReportFormSchema` and a hard gate on submit — and never sent it. On a
146
+ * surface where a false report gets someone listed publicly and has account
147
+ * consequences for the reporter, the attestation is precisely the record you
148
+ * want to keep, and it existed only as a client-side gate that any direct
149
+ * call to the route bypassed entirely.
150
+ *
151
+ * Optional on the document so the rows written before 2026-08-29 stay valid;
152
+ * absent means "not recorded", which is honest, and is NOT the same as false.
153
+ */
154
+ reporterAttested?: boolean;
141
155
  status: ScammerStatus;
142
156
  /** UID of admin/employee who verified or rejected. */
143
157
  verifiedBy?: string;
@@ -342,7 +356,7 @@ export declare const SCAMMER_INDEXED_FIELDS: readonly ["status", "scamType", "sc
342
356
  export declare const DEFAULT_SCAMMER_DATA: Partial<ScammerDocument>;
343
357
  /** Max pending submissions per user — prevents spam flooding. */
344
358
  export declare const MAX_PENDING_SCAMMER_REPORTS_PER_USER = 5;
345
- export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon">;
359
+ export type ScammerCreateInput = Pick<ScammerDocument, "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "scamType" | "scamPlatform" | "description" | "amountLost" | "itemInvolved" | "evidence" | "reportedBy" | "reportedByAnon" | "reporterAttested">;
346
360
  export type ScammerAdminUpdateInput = Partial<Pick<ScammerDocument, "status" | "verifiedBy" | "verifiedAt" | "verificationNote" | "relatedScammerIds" | "mergedFromIds" | "tags" | "displayNames" | "phones" | "upiIds" | "emails" | "socialMedia" | "isContested">>;
347
361
  export declare const SCAMMER_FIELDS: {
348
362
  readonly ID: "id";
@@ -5,7 +5,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
5
5
  upiIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
6
  emails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
7
7
  scamType: z.ZodString;
8
- scamPlatform: z.ZodOptional<z.ZodString>;
8
+ scamPlatform: z.ZodString;
9
9
  amountLost: z.ZodOptional<z.ZodNumber>;
10
10
  itemInvolved: z.ZodOptional<z.ZodString>;
11
11
  description: z.ZodString;
@@ -15,9 +15,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
15
15
  displayName: string;
16
16
  description: string;
17
17
  scamType: string;
18
+ scamPlatform: string;
18
19
  agreed: true;
19
20
  reportedByAnon?: boolean | undefined;
20
- scamPlatform?: string | undefined;
21
21
  amountLost?: number | undefined;
22
22
  itemInvolved?: string | undefined;
23
23
  phones?: string[] | undefined;
@@ -27,9 +27,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
27
27
  displayName: string;
28
28
  description: string;
29
29
  scamType: string;
30
+ scamPlatform: string;
30
31
  agreed: true;
31
32
  reportedByAnon?: boolean | undefined;
32
- scamPlatform?: string | undefined;
33
33
  amountLost?: number | undefined;
34
34
  itemInvolved?: string | undefined;
35
35
  phones?: string[] | undefined;
@@ -39,9 +39,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
39
39
  displayName: string;
40
40
  description: string;
41
41
  scamType: string;
42
+ scamPlatform: string;
42
43
  agreed: true;
43
44
  reportedByAnon?: boolean | undefined;
44
- scamPlatform?: string | undefined;
45
45
  amountLost?: number | undefined;
46
46
  itemInvolved?: string | undefined;
47
47
  phones?: string[] | undefined;
@@ -51,9 +51,9 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
51
51
  displayName: string;
52
52
  description: string;
53
53
  scamType: string;
54
+ scamPlatform: string;
54
55
  agreed: true;
55
56
  reportedByAnon?: boolean | undefined;
56
- scamPlatform?: string | undefined;
57
57
  amountLost?: number | undefined;
58
58
  itemInvolved?: string | undefined;
59
59
  phones?: string[] | undefined;
@@ -45,8 +45,17 @@ export const scamReportFormSchema = z
45
45
  scamType: annotate(z.string().trim().min(1, "Choose what kind of scam this was."), {
46
46
  section: "what", sectionLabel: "What happened", sectionRequired: true, quick: true, order: 1, row: "pair",
47
47
  }),
48
- scamPlatform: annotate(z.string().trim().max(120).optional(), {
49
- section: "what", order: 2, row: "pair",
48
+ /*
49
+ * Required, matching `POST /api/scams/reports` (`z.string().min(1)`).
50
+ *
51
+ * It was `.optional()` here, so a report with no platform passed every
52
+ * client check and came back a 400. The page papered over it with a
53
+ * hand-written `if (!form.scamType || !form.scamPlatform)` guard — which is
54
+ * the same drift as the address postal rule: two rules for one field, and
55
+ * the looser one is the client's.
56
+ */
57
+ scamPlatform: annotate(z.string().trim().min(1, "Choose where this happened.").max(120), {
58
+ section: "what", order: 2, row: "pair", kind: "select",
50
59
  }),
51
60
  /*
52
61
  * Coerced and bounded. It arrives from a text input and is rendered on the
@@ -62,10 +71,16 @@ export const scamReportFormSchema = z
62
71
  itemInvolved: annotate(z.string().trim().max(200).optional(), {
63
72
  section: "what", order: 4, row: "pair",
64
73
  }),
74
+ /*
75
+ * 100, matching the route. It was 30 here, so a 31-character description
76
+ * satisfied every client check and came back a 400 — and the page's own
77
+ * hand-written guard used 100, which is the number that was actually
78
+ * enforced. Two rules, three values.
79
+ */
65
80
  description: annotate(z
66
81
  .string()
67
82
  .trim()
68
- .min(30, "Describe what happened in at least 30 characters — this is the evidence.")
83
+ .min(100, "Describe what happened in at least 100 characters — this is the evidence.")
69
84
  .max(5000, "Keep the description under 5000 characters."), { section: "what", order: 5, row: "full", kind: "textarea" }),
70
85
  reportedByAnon: annotate(z.boolean().optional(), {
71
86
  section: "declare", sectionLabel: "Declaration", order: 1, row: "quarter",
@@ -0,0 +1,49 @@
1
+ import type { NavPortal, SidebarNavGroup } from "../../../_internal/shared/features/layout/types";
2
+ import type { ActionIndexEntry } from "./types";
3
+ /** A nav group set, with the portal it belongs to. */
4
+ export interface PortalNavGroups {
5
+ portal: NavPortal;
6
+ /** Human name for the breadcrumb — "Admin", "Seller", "Account". */
7
+ portalLabel: string;
8
+ groups: readonly SidebarNavGroup[];
9
+ }
10
+ /**
11
+ * Every sidebar entry, as an index entry.
12
+ *
13
+ * `sectionPath` is the breadcrumb a result row shows — "Admin › System › Site
14
+ * Settings" — and it is the reason the group title is worth carrying: two
15
+ * portals both have "Orders", and without the path a result list shows the
16
+ * same word twice with no way to tell which one you want.
17
+ */
18
+ export declare function deriveNavEntries(sources: readonly PortalNavGroups[]): ActionIndexEntry[];
19
+ /** One quick action, as the registry stores it. */
20
+ export interface QuickActionSource {
21
+ id: string;
22
+ label: string;
23
+ description?: string;
24
+ keywords?: string[];
25
+ href?: string;
26
+ routeKey?: string;
27
+ iconKey?: string;
28
+ requiredPermission?: string;
29
+ requiredRole?: string;
30
+ requiresAuth?: boolean;
31
+ }
32
+ /**
33
+ * Quick actions, as index entries.
34
+ *
35
+ * 🛑 An action with no resolvable href is DROPPED, not indexed with a blank
36
+ * one. `DASHBOARD_QUICK_ACTION_META`'s `routeKey` is dead — every page
37
+ * re-declares its own `*_QUICK_ACTION_HREFS` map instead — so a registry entry
38
+ * may genuinely have nowhere to go. Indexing it anyway produces a search
39
+ * result that does nothing when clicked, which is worse than not finding it.
40
+ */
41
+ export declare function deriveQuickActionEntries(actions: readonly QuickActionSource[], portal: NavPortal, portalLabel: string, hrefFor: (action: QuickActionSource) => string | undefined): ActionIndexEntry[];
42
+ /**
43
+ * Assemble the static base.
44
+ *
45
+ * Deduplicated by id, first writer winning, so a quick action that shares an
46
+ * id with a nav entry does not produce two rows for one destination — and the
47
+ * nav entry, being passed first, is the one that survives.
48
+ */
49
+ export declare function buildActionIndexBase(...groups: readonly ActionIndexEntry[][]): ActionIndexEntry[];
@@ -0,0 +1,121 @@
1
+ /*
2
+ * WHY: An index of "everything a user can do" is worth having exactly once and
3
+ * worth maintaining never. Hand-writing 260 entries beside the nav arrays
4
+ * they duplicate produces the drift this plan has spent every wave
5
+ * undoing — ten hand-written enumerations of one union (Root Cause #61),
6
+ * three option arrays that had each silently lost a value (W3c), fifteen
7
+ * postal rules (W5).
8
+ * WHAT: Derive the static base FROM the nav groups and the quick-action
9
+ * registry, so an entry cannot exist without its nav item and cannot
10
+ * go stale when one is renamed.
11
+ *
12
+ * W6 is what makes this possible: every nav item now carries a `description`,
13
+ * `keywords` and a derived `id`. Before that there was nothing to derive
14
+ * from — which is the actual reason `Search.tsx`'s finished `quickLinks` path
15
+ * was never wired to anything.
16
+ *
17
+ * EXPORTS: deriveNavEntries, deriveQuickActionEntries, buildActionIndexBase
18
+ *
19
+ * @tag domain:search
20
+ * @tag layer:shared
21
+ * @tag pattern:none
22
+ * @tag access:isomorphic
23
+ * @tag consumers:action-index route,CommandPalette
24
+ * @tag sideEffects:none
25
+ */
26
+ /**
27
+ * Every sidebar entry, as an index entry.
28
+ *
29
+ * `sectionPath` is the breadcrumb a result row shows — "Admin › System › Site
30
+ * Settings" — and it is the reason the group title is worth carrying: two
31
+ * portals both have "Orders", and without the path a result list shows the
32
+ * same word twice with no way to tell which one you want.
33
+ */
34
+ export function deriveNavEntries(sources) {
35
+ const entries = [];
36
+ for (const { portal, portalLabel, groups } of sources) {
37
+ for (const group of groups) {
38
+ for (const item of group.items) {
39
+ entries.push({
40
+ /*
41
+ * The nav item's own derived id, reused rather than re-derived. It
42
+ * is already `nav-{portal}-{slug}`, so prefixing the kind keeps the
43
+ * index's `{portal}:{kind}:{slug}` convention without inventing a
44
+ * second identity for the same thing.
45
+ */
46
+ id: item.id ?? `${portal}:nav:${item.href}`,
47
+ kind: "nav",
48
+ portal,
49
+ label: item.label,
50
+ description: item.description,
51
+ keywords: item.keywords,
52
+ href: item.href,
53
+ sectionPath: `${portalLabel} › ${group.title}`,
54
+ ...(item.requiredPermission
55
+ ? { requiredPermission: item.requiredPermission }
56
+ : {}),
57
+ });
58
+ }
59
+ }
60
+ }
61
+ return entries;
62
+ }
63
+ /**
64
+ * Quick actions, as index entries.
65
+ *
66
+ * 🛑 An action with no resolvable href is DROPPED, not indexed with a blank
67
+ * one. `DASHBOARD_QUICK_ACTION_META`'s `routeKey` is dead — every page
68
+ * re-declares its own `*_QUICK_ACTION_HREFS` map instead — so a registry entry
69
+ * may genuinely have nowhere to go. Indexing it anyway produces a search
70
+ * result that does nothing when clicked, which is worse than not finding it.
71
+ */
72
+ export function deriveQuickActionEntries(actions, portal, portalLabel, hrefFor) {
73
+ const entries = [];
74
+ for (const action of actions) {
75
+ const href = action.href ?? hrefFor(action);
76
+ if (!href)
77
+ continue;
78
+ entries.push({
79
+ id: `${portal}:action:${action.id}`,
80
+ kind: "action",
81
+ portal,
82
+ label: action.label,
83
+ description: action.description,
84
+ keywords: action.keywords,
85
+ href,
86
+ sectionPath: `${portalLabel} › Quick actions`,
87
+ ...(action.iconKey ? { iconName: action.iconKey } : {}),
88
+ ...(action.requiredPermission
89
+ ? { requiredPermission: action.requiredPermission }
90
+ : {}),
91
+ ...(action.requiredRole ? { requiredRole: action.requiredRole } : {}),
92
+ /*
93
+ * A quick action is a shortcut to something already reachable from the
94
+ * nav, so it ranks BELOW the nav entry for the same screen. Searching
95
+ * "orders" should land on Orders, not on "Create an order".
96
+ */
97
+ weight: -10,
98
+ });
99
+ }
100
+ return entries;
101
+ }
102
+ /**
103
+ * Assemble the static base.
104
+ *
105
+ * Deduplicated by id, first writer winning, so a quick action that shares an
106
+ * id with a nav entry does not produce two rows for one destination — and the
107
+ * nav entry, being passed first, is the one that survives.
108
+ */
109
+ export function buildActionIndexBase(...groups) {
110
+ const seen = new Set();
111
+ const out = [];
112
+ for (const list of groups) {
113
+ for (const entry of list) {
114
+ if (seen.has(entry.id))
115
+ continue;
116
+ seen.add(entry.id);
117
+ out.push(entry);
118
+ }
119
+ }
120
+ return out;
121
+ }
@@ -0,0 +1,42 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { type ActionIndexControl, type ActionIndexEntry, type ActionIndexOverride } from "./types";
3
+ /** The stored document. `id` is always `ACTION_INDEX_DOC_ID`. */
4
+ export interface ActionIndexDocument extends ActionIndexControl {
5
+ id: string;
6
+ updatedAt?: Date;
7
+ updatedBy?: string;
8
+ }
9
+ export declare class ActionIndexRepository extends BaseRepository<ActionIndexDocument> {
10
+ constructor();
11
+ /**
12
+ * The control document, or an empty one.
13
+ *
14
+ * 🛑 Returns EMPTY rather than null, and that is load-bearing:
15
+ * `mergeActionIndex` treats a missing control as "no overrides", so a site
16
+ * that has never opened the admin screen still gets the full static base.
17
+ * A null here would make every caller write the same `?? EMPTY` and one of
18
+ * them would forget, blanking the search.
19
+ */
20
+ getControl(): Promise<ActionIndexControl>;
21
+ /**
22
+ * Overwrite one entry's override.
23
+ *
24
+ * Merged into the stored map rather than replacing it, because the admin
25
+ * screen edits one row at a time and a whole-map write would drop every
26
+ * other override made in a different browser tab — the lost-update shape
27
+ * that `mergeLotteryConfig` exists to prevent on the lottery side.
28
+ */
29
+ setOverride(entryId: string, override: ActionIndexOverride, actorUid: string): Promise<void>;
30
+ /** Drop an override, returning the entry to whatever the code says. */
31
+ clearOverride(entryId: string, actorUid: string): Promise<void>;
32
+ /**
33
+ * Replace the admin-authored entries.
34
+ *
35
+ * Whole-array, unlike `setOverride`: the custom list is edited as a list in
36
+ * one screen, and its ids are chosen by the admin rather than derived, so
37
+ * there is no stable key to merge on.
38
+ */
39
+ setCustomEntries(custom: ActionIndexEntry[], actorUid: string): Promise<void>;
40
+ private write;
41
+ }
42
+ export declare const actionIndexRepository: ActionIndexRepository;
@@ -0,0 +1,100 @@
1
+ /*
2
+ * WHY: D7 — the action index is a Firestore CONTROL PLANE over a static TS
3
+ * base, not a second copy of it. An admin can hide, rename, re-describe,
4
+ * retag, reorder and author entries; the code keeps `kind`, `href` and
5
+ * every permission field for built-ins, because renaming a toggle is
6
+ * content and repointing a built-in route is routing.
7
+ * WHAT: The `actionIndex/global` singleton and its repository.
8
+ *
9
+ * ## Deliberately NOT folded into `siteSettings`
10
+ *
11
+ * That document is read by an unauthenticated, edge-cached route
12
+ * (`GET /api/site-settings`, `s-maxage=600`). This one is projected by role and
13
+ * must never be served from a shared cache — putting them together is one
14
+ * refactor away from publishing the admin site map, which is exactly the class
15
+ * of defect Root Cause #70 records.
16
+ *
17
+ * ## One document, ~85 KB
18
+ *
19
+ * ~380 entries at full size, comfortably inside Firestore's 1 MB ceiling, and
20
+ * one read rather than a query. The read path caches for 5 minutes and is
21
+ * fetched **once per session per portal** — a read per keystroke would exhaust
22
+ * the 50k/day free tier at roughly thirty sessions, and typeahead matching has
23
+ * to be sub-16ms anyway, which a network hop is not.
24
+ *
25
+ * EXPORTS: ActionIndexRepository, actionIndexRepository
26
+ *
27
+ * @tag domain:search
28
+ * @tag layer:repository
29
+ * @tag pattern:singleton-doc
30
+ * @tag access:server
31
+ * @tag consumers:/api/admin/action-index,/api/action-index
32
+ * @tag sideEffects:firestore
33
+ */
34
+ import { BaseRepository } from "../../../providers/db-firebase";
35
+ import { ACTION_INDEX_COLLECTION, ACTION_INDEX_DOC_ID, } from "./types";
36
+ /** What an empty control plane looks like — no overrides, no custom entries. */
37
+ const EMPTY_CONTROL = { entries: {}, custom: [] };
38
+ export class ActionIndexRepository extends BaseRepository {
39
+ constructor() {
40
+ super(ACTION_INDEX_COLLECTION);
41
+ }
42
+ /**
43
+ * The control document, or an empty one.
44
+ *
45
+ * 🛑 Returns EMPTY rather than null, and that is load-bearing:
46
+ * `mergeActionIndex` treats a missing control as "no overrides", so a site
47
+ * that has never opened the admin screen still gets the full static base.
48
+ * A null here would make every caller write the same `?? EMPTY` and one of
49
+ * them would forget, blanking the search.
50
+ */
51
+ async getControl() {
52
+ const doc = await this.findById(ACTION_INDEX_DOC_ID);
53
+ if (!doc)
54
+ return EMPTY_CONTROL;
55
+ return { entries: doc.entries ?? {}, custom: doc.custom ?? [] };
56
+ }
57
+ /**
58
+ * Overwrite one entry's override.
59
+ *
60
+ * Merged into the stored map rather than replacing it, because the admin
61
+ * screen edits one row at a time and a whole-map write would drop every
62
+ * other override made in a different browser tab — the lost-update shape
63
+ * that `mergeLotteryConfig` exists to prevent on the lottery side.
64
+ */
65
+ async setOverride(entryId, override, actorUid) {
66
+ const control = await this.getControl();
67
+ const next = {
68
+ ...control,
69
+ entries: { ...control.entries, [entryId]: override },
70
+ };
71
+ await this.write(next, actorUid);
72
+ }
73
+ /** Drop an override, returning the entry to whatever the code says. */
74
+ async clearOverride(entryId, actorUid) {
75
+ const control = await this.getControl();
76
+ const entries = { ...control.entries };
77
+ delete entries[entryId];
78
+ await this.write({ ...control, entries }, actorUid);
79
+ }
80
+ /**
81
+ * Replace the admin-authored entries.
82
+ *
83
+ * Whole-array, unlike `setOverride`: the custom list is edited as a list in
84
+ * one screen, and its ids are chosen by the admin rather than derived, so
85
+ * there is no stable key to merge on.
86
+ */
87
+ async setCustomEntries(custom, actorUid) {
88
+ const control = await this.getControl();
89
+ await this.write({ ...control, custom }, actorUid);
90
+ }
91
+ async write(control, actorUid) {
92
+ const payload = { ...control, updatedAt: new Date(), updatedBy: actorUid };
93
+ const existing = await this.findById(ACTION_INDEX_DOC_ID);
94
+ if (existing)
95
+ await this.update(ACTION_INDEX_DOC_ID, payload);
96
+ else
97
+ await this.createWithId(ACTION_INDEX_DOC_ID, payload);
98
+ }
99
+ }
100
+ export const actionIndexRepository = new ActionIndexRepository();
@@ -0,0 +1,41 @@
1
+ import type { SiteSettingsTabId } from "../../admin/constants/site-settings-tabs";
2
+ import type { ActionIndexEntry } from "./types";
3
+ /** One control on the site-settings screen. */
4
+ export interface SettingsIndexSource {
5
+ /** Slug — becomes both the entry id and the `#anchor`. */
6
+ slug: string;
7
+ label: string;
8
+ description: string;
9
+ keywords: string[];
10
+ tab: SiteSettingsTabId;
11
+ /** `toggle` for an on/off switch, `setting` for a value. */
12
+ kind: "toggle" | "setting";
13
+ /**
14
+ * Whether the view renders `id="setting-{slug}"` for this control.
15
+ *
16
+ * 🛑 Opt-in, and audited. An anchor that matches no `id=` in the DOM scrolls
17
+ * nowhere and reads as the search having missed — so claiming one you have
18
+ * not added is worse than not claiming it. An entry without one deep-links
19
+ * to its TAB, which is truthful: "this lives on the Fees tab" is a smaller
20
+ * promise than "here is the control", and it is one the page can keep.
21
+ */
22
+ anchored?: boolean;
23
+ }
24
+ /**
25
+ * The controls worth finding.
26
+ *
27
+ * Deliberately not all ~160: a search result list is only useful while it is
28
+ * short, and "Hero title" is not something anyone hunts for — they open the
29
+ * About tab and see it. What is here is what someone arrives ALREADY looking
30
+ * for, usually in a hurry, and usually not knowing which tab it is filed under.
31
+ */
32
+ export declare const SITE_SETTINGS_ENTRIES: readonly SettingsIndexSource[];
33
+ /**
34
+ * The settings entries, as index entries.
35
+ *
36
+ * `deepLink` carries the whole query-and-hash — `?tab=fees#setting-cod-enabled`
37
+ * — rather than just the tab, because landing on the right tab and leaving the
38
+ * reader to find the control among forty others is most of the way to not
39
+ * having searched at all.
40
+ */
41
+ export declare function deriveSettingsEntries(settingsHref: string, requiredPermission?: string): ActionIndexEntry[];