@mohasinac/appkit 4.30.19 → 4.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  2. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  4. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -87
  5. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  6. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  7. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  8. package/dist/_internal/server/features/auth/permissions.js +3 -2
  9. package/dist/_internal/server/features/categories/data.js +15 -4
  10. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  11. package/dist/_internal/server/features/classified/index.js +0 -1
  12. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  13. package/dist/_internal/server/features/digital-code/index.js +0 -1
  14. package/dist/_internal/server/features/live/index.d.ts +0 -1
  15. package/dist/_internal/server/features/live/index.js +0 -1
  16. package/dist/_internal/server/features/products/data.js +15 -5
  17. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  18. package/dist/_internal/server/features/products/list-public.js +95 -7
  19. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  20. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  21. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  22. package/dist/_internal/server/features/tester/visibility.js +2 -2
  23. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  24. package/dist/_internal/server/functions/scheduled.js +27 -1
  25. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  26. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  27. package/dist/_internal/server/jobs/core/index.js +2 -0
  28. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  29. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  30. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  31. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  34. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  35. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  36. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  37. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  38. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  39. package/dist/_internal/shared/actions/action-registry.js +69 -22
  40. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  41. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  42. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  43. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  44. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  45. package/dist/_internal/shared/features/layout/types.js +19 -1
  46. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  47. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  48. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  49. package/dist/_internal/shared/features/offers/config.js +73 -2
  50. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  51. package/dist/_internal/shared/features/orders/schema.js +22 -2
  52. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  53. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  54. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  55. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  56. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  57. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  58. package/dist/client.d.ts +37 -20
  59. package/dist/client.js +36 -14
  60. package/dist/configs/next.js +10 -2
  61. package/dist/constants/api-endpoints.d.ts +18 -54
  62. package/dist/constants/api-endpoints.js +6 -26
  63. package/dist/constants/field-names.d.ts +6 -24
  64. package/dist/constants/field-names.js +10 -27
  65. package/dist/constants/geo/countries.d.ts +50 -0
  66. package/dist/constants/geo/countries.js +129 -0
  67. package/dist/constants/geo/subdivisions.d.ts +13 -0
  68. package/dist/constants/geo/subdivisions.js +104 -0
  69. package/dist/constants/index.d.ts +2 -2
  70. package/dist/constants/index.js +2 -2
  71. package/dist/constants/table-keys.d.ts +24 -0
  72. package/dist/constants/table-keys.js +28 -0
  73. package/dist/contracts/extend.d.ts +0 -9
  74. package/dist/contracts/index.d.ts +1 -1
  75. package/dist/contracts/realtime-session.js +7 -0
  76. package/dist/core/browser.d.ts +1 -1
  77. package/dist/core/browser.js +1 -1
  78. package/dist/core/index.d.ts +1 -1
  79. package/dist/core/index.js +1 -1
  80. package/dist/core/site-config.d.ts +0 -3
  81. package/dist/core/site-config.js +0 -3
  82. package/dist/errors/messages.d.ts +0 -8
  83. package/dist/errors/messages.js +0 -9
  84. package/dist/features/about/schemas/index.d.ts +8 -8
  85. package/dist/features/account/components/AddressFilters.js +32 -11
  86. package/dist/features/account/components/AddressForm.d.ts +26 -25
  87. package/dist/features/account/components/AddressForm.js +81 -103
  88. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  89. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  90. package/dist/features/account/components/UserSupportView.js +89 -58
  91. package/dist/features/account/components/index.d.ts +1 -7
  92. package/dist/features/account/components/index.js +0 -3
  93. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  94. package/dist/features/account/hooks/useAddresses.js +4 -6
  95. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  96. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  97. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  98. package/dist/features/account/index.d.ts +0 -3
  99. package/dist/features/account/index.js +0 -3
  100. package/dist/features/account/schemas/index.d.ts +0 -92
  101. package/dist/features/account/schemas/index.js +42 -20
  102. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  103. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  104. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  105. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  106. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  107. package/dist/features/addresses/schemas/address-form.js +212 -0
  108. package/dist/features/addresses/schemas/index.d.ts +1 -0
  109. package/dist/features/addresses/schemas/index.js +8 -0
  110. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  111. package/dist/features/admin/actions/index.d.ts +0 -1
  112. package/dist/features/admin/actions/index.js +0 -1
  113. package/dist/features/admin/actions/notification-actions.js +1 -41
  114. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  115. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  116. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  117. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  118. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  119. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  120. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  121. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  122. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  123. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  124. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  125. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  126. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  127. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  128. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  129. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  130. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  131. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  132. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  133. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  134. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  135. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  136. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  137. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  138. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  139. package/dist/features/admin/components/index.d.ts +2 -2
  140. package/dist/features/admin/components/index.js +1 -1
  141. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  142. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  143. package/dist/features/admin/index.d.ts +0 -3
  144. package/dist/features/admin/index.js +0 -2
  145. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  146. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  147. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  148. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  149. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  150. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  151. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  152. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  153. package/dist/features/admin/schemas/firestore.js +15 -151
  154. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  155. package/dist/features/admin/server.d.ts +0 -4
  156. package/dist/features/admin/server.js +0 -2
  157. package/dist/features/admin/types/index.d.ts +0 -7
  158. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  159. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  160. package/dist/features/auth/actions/index.d.ts +0 -1
  161. package/dist/features/auth/actions/index.js +0 -1
  162. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  163. package/dist/features/auth/auth-helpers.js +5 -7
  164. package/dist/features/auth/components/Guards.js +2 -1
  165. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  166. package/dist/features/auth/components/LoginForm.js +1 -3
  167. package/dist/features/auth/components/RegisterForm.js +62 -19
  168. package/dist/features/auth/permissions/constants.d.ts +27 -1
  169. package/dist/features/auth/role-predicates.d.ts +75 -7
  170. package/dist/features/auth/role-predicates.js +28 -2
  171. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  172. package/dist/features/auth/schemas/index.d.ts +1 -1
  173. package/dist/features/auth/schemas/index.js +44 -8
  174. package/dist/features/auth/types/index.d.ts +14 -1
  175. package/dist/features/blog/api/route.js +10 -2
  176. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  177. package/dist/features/blog/hooks/useBlog.js +2 -2
  178. package/dist/features/blog/schemas/index.d.ts +4 -0
  179. package/dist/features/blog/schemas/index.js +2 -0
  180. package/dist/features/blog/types/index.d.ts +1 -1
  181. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  182. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  183. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  184. package/dist/features/catalogue/schemas/validation.js +47 -10
  185. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  186. package/dist/features/categories/schemas/bundle-form.js +149 -0
  187. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  188. package/dist/features/classified/components/ClassifiedDetailPageView.js +37 -16
  189. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  190. package/dist/features/consultation/schemas/index.js +34 -10
  191. package/dist/features/corporate/schemas/index.js +36 -15
  192. package/dist/features/filters/FilterPanel.js +3 -3
  193. package/dist/features/filters/RangeFilter.d.ts +10 -0
  194. package/dist/features/filters/RangeFilter.js +41 -4
  195. package/dist/features/homepage/components/MarketplaceHomepageView.js +24 -5
  196. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  197. package/dist/features/layout/BottomActions.js +84 -11
  198. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  199. package/dist/features/layout/BottomActionsContext.js +1 -1
  200. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  201. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  202. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  203. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  204. package/dist/features/lottery/schemas/config-write.js +69 -9
  205. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  206. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  207. package/dist/features/orders/schemas/index.d.ts +2 -2
  208. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  209. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  210. package/dist/features/products/components/MakeOfferButton.js +33 -11
  211. package/dist/features/products/components/ProductDetailActions.js +0 -1
  212. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  213. package/dist/features/products/components/ProductDetailPageView.js +8 -5
  214. package/dist/features/products/constants/action-defs.d.ts +1 -2
  215. package/dist/features/products/constants/action-defs.js +40 -43
  216. package/dist/features/products/repository/products.repository.d.ts +5 -1
  217. package/dist/features/products/repository/products.repository.js +23 -1
  218. package/dist/features/products/schemas/firestore.d.ts +7 -3
  219. package/dist/features/products/schemas/product-features.validators.js +3 -0
  220. package/dist/features/promotions/components/CouponCard.js +8 -7
  221. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  222. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  223. package/dist/features/scams/schemas/report-form.js +18 -3
  224. package/dist/features/search/action-index/derive.d.ts +49 -0
  225. package/dist/features/search/action-index/derive.js +121 -0
  226. package/dist/features/search/action-index/repository.d.ts +42 -0
  227. package/dist/features/search/action-index/repository.js +100 -0
  228. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  229. package/dist/features/search/action-index/settings-entries.js +268 -0
  230. package/dist/features/search/action-index/types.d.ts +81 -0
  231. package/dist/features/search/action-index/types.js +102 -0
  232. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  233. package/dist/features/search/action-index/validate-entry.js +114 -0
  234. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  235. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  236. package/dist/features/search/repository/search.repository.js +3 -1
  237. package/dist/features/seller/actions/offer-actions.js +43 -24
  238. package/dist/features/seller/actions/seller-actions.js +29 -5
  239. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  240. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  241. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  242. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  243. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  244. package/dist/features/seller/components/SellerArtView.js +4 -119
  245. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  246. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  247. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  248. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  249. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  250. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  251. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  252. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  253. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  254. package/dist/features/seller/components/SellerLiveView.js +4 -126
  255. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  256. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  257. package/dist/features/seller/components/SellerStickersView.js +4 -119
  258. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  259. package/dist/features/seller/components/SellerSupportView.js +100 -0
  260. package/dist/features/seller/components/index.d.ts +4 -8
  261. package/dist/features/seller/components/index.js +2 -4
  262. package/dist/features/seller/index.d.ts +0 -1
  263. package/dist/features/seller/index.js +0 -1
  264. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  265. package/dist/features/seller/schemas/coupon-form.js +155 -23
  266. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  267. package/dist/features/seller/schemas/offer-forms.js +28 -12
  268. package/dist/features/shell/FormShell.js +2 -2
  269. package/dist/features/shell/QuickFormDrawer.js +2 -2
  270. package/dist/features/shell/SectionForm.d.ts +19 -1
  271. package/dist/features/shell/SectionForm.js +104 -17
  272. package/dist/features/shell/build-sections.d.ts +23 -0
  273. package/dist/features/shell/build-sections.js +106 -8
  274. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  275. package/dist/features/shell/field-ui-meta.js +14 -7
  276. package/dist/features/shell/form-sections.d.ts +46 -0
  277. package/dist/features/shell/form-sections.js +114 -0
  278. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  279. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  280. package/dist/features/shipments/schemas/validation.js +44 -13
  281. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  282. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  283. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  284. package/dist/features/stores/actions/store-query-actions.js +3 -2
  285. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  286. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  287. package/dist/features/support/repository/support.repository.d.ts +21 -0
  288. package/dist/features/support/repository/support.repository.js +63 -0
  289. package/dist/features/support/schemas/firestore.d.ts +27 -2
  290. package/dist/features/support/schemas/firestore.js +2 -0
  291. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  292. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  293. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  294. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  295. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  296. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  297. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  298. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  299. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  300. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +275 -29
  301. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +48 -12
  302. package/dist/http/api-handler.js +2 -1
  303. package/dist/index.d.ts +24 -103
  304. package/dist/index.js +33 -141
  305. package/dist/jobs.d.ts +1 -1
  306. package/dist/jobs.js +1 -1
  307. package/dist/monitoring/browser.d.ts +0 -2
  308. package/dist/monitoring/browser.js +12 -1
  309. package/dist/monitoring/index.d.ts +0 -2
  310. package/dist/monitoring/index.js +0 -1
  311. package/dist/next/api/routeHandler.js +6 -3
  312. package/dist/next/index.d.ts +1 -2
  313. package/dist/next/index.js +0 -1
  314. package/dist/next/middleware/index.d.ts +0 -2
  315. package/dist/next/middleware/index.js +0 -1
  316. package/dist/next/routing/route-map.d.ts +8 -7
  317. package/dist/next/routing/route-map.js +10 -3
  318. package/dist/providers/db-firebase/admin.js +15 -1
  319. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  320. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  321. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  322. package/dist/react/hooks/usePendingFilters.js +5 -3
  323. package/dist/react/hooks/usePendingTable.js +15 -3
  324. package/dist/repositories/index.d.ts +0 -1
  325. package/dist/repositories/index.js +0 -1
  326. package/dist/schemas/registry.d.ts +197 -232
  327. package/dist/schemas/registry.js +5 -6
  328. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  329. package/dist/security/authorization.d.ts +9 -0
  330. package/dist/security/authorization.js +16 -2
  331. package/dist/security/browser.d.ts +1 -3
  332. package/dist/security/browser.js +5 -2
  333. package/dist/security/index.d.ts +1 -3
  334. package/dist/security/index.js +19 -2
  335. package/dist/security/pii-encrypt.js +16 -0
  336. package/dist/security/pii-schemas.d.ts +0 -14
  337. package/dist/security/pii-schemas.js +7 -15
  338. package/dist/seed/_helpers/category-forest.js +2 -2
  339. package/dist/seed/_helpers/media.d.ts +19 -0
  340. package/dist/seed/_helpers/media.js +75 -0
  341. package/dist/seed/blog-posts-seed-data.js +17 -17
  342. package/dist/seed/carousel-slides-seed-data.js +7 -7
  343. package/dist/seed/cart-seed-data.js +13 -13
  344. package/dist/seed/categories-seed-data.js +10 -10
  345. package/dist/seed/events-seed-data.js +24 -24
  346. package/dist/seed/factories/product.factory.js +4 -4
  347. package/dist/seed/factories/store.factory.js +3 -3
  348. package/dist/seed/factories/user.factory.js +2 -2
  349. package/dist/seed/grouped-listings-seed-data.js +11 -11
  350. package/dist/seed/homepage-sections-seed-data.js +2 -2
  351. package/dist/seed/index.d.ts +0 -1
  352. package/dist/seed/index.js +0 -1
  353. package/dist/seed/manifest.d.ts +1 -1
  354. package/dist/seed/manifest.js +0 -5
  355. package/dist/seed/notifications-seed-data.js +20 -1
  356. package/dist/seed/orders-seed-data.js +2 -2
  357. package/dist/seed/products-art-seed-data.js +16 -16
  358. package/dist/seed/products-auctions-seed-data.js +34 -34
  359. package/dist/seed/products-classifieds-seed-data.js +35 -37
  360. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  361. package/dist/seed/products-live-items-seed-data.js +11 -11
  362. package/dist/seed/products-preorders-seed-data.js +13 -13
  363. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  364. package/dist/seed/products-standard-seed-data.js +54 -54
  365. package/dist/seed/products-stickers-seed-data.js +15 -15
  366. package/dist/seed/reviews-seed-data.js +4 -4
  367. package/dist/seed/scammers-seed-data.js +3 -3
  368. package/dist/seed/site-settings-seed-data.js +11 -24
  369. package/dist/seed/store-extensions-seed-data.js +10 -10
  370. package/dist/seed/stores-seed-data.js +11 -11
  371. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  372. package/dist/seed/support-tickets-seed-data.js +20 -1
  373. package/dist/seed/users-seed-data.js +20 -20
  374. package/dist/server-entry.d.ts +0 -1
  375. package/dist/server-entry.js +0 -1
  376. package/dist/server.d.ts +9 -6
  377. package/dist/server.js +7 -13
  378. package/dist/styles.css +248 -68
  379. package/dist/tailwind-utilities.css +1 -1
  380. package/dist/tokens/themes/default-dark.js +2 -2
  381. package/dist/tokens/themes/default-light.js +2 -2
  382. package/dist/tokens/tokens.css +24 -4
  383. package/dist/ui/components/ActionRow.d.ts +50 -0
  384. package/dist/ui/components/ActionRow.js +53 -0
  385. package/dist/ui/components/ActionRow.style.css +145 -0
  386. package/dist/ui/components/Drawer.d.ts +10 -0
  387. package/dist/ui/components/Drawer.js +2 -1
  388. package/dist/ui/components/FilterDrawer.js +4 -1
  389. package/dist/ui/components/FilterDrawer.style.css +6 -12
  390. package/dist/ui/components/FormActionBar.js +14 -1
  391. package/dist/ui/components/FormActionBar.style.css +12 -11
  392. package/dist/ui/components/ListingLayout.js +8 -1
  393. package/dist/ui/components/ListingLayout.style.css +6 -13
  394. package/dist/ui/components/Modal.d.ts +15 -1
  395. package/dist/ui/components/Modal.js +17 -3
  396. package/dist/ui/components/QuickCreateModal.js +2 -2
  397. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  398. package/dist/ui/components/RecordDetailModal.js +2 -2
  399. package/dist/ui/components/SideDrawer.d.ts +8 -0
  400. package/dist/ui/components/SideDrawer.js +2 -1
  401. package/dist/ui/components/SideDrawer.style.css +6 -3
  402. package/dist/ui/components/SideModal.d.ts +11 -1
  403. package/dist/ui/components/SideModal.js +3 -2
  404. package/dist/ui/components/SideModal.style.css +14 -0
  405. package/dist/ui/components/Typography.style.css +33 -24
  406. package/dist/ui/components/index.style.css +1 -0
  407. package/dist/ui/forms/FormShell.style.css +260 -311
  408. package/dist/ui/index.d.ts +2 -0
  409. package/dist/ui/index.js +1 -0
  410. package/dist/utils/index.d.ts +0 -1
  411. package/dist/utils/index.js +0 -1
  412. package/dist/validation/index.d.ts +1 -1
  413. package/dist/validation/index.js +1 -1
  414. package/dist/validation/schemas.d.ts +0 -19
  415. package/dist/validation/schemas.js +8 -19
  416. package/package.json +1 -1
  417. package/scripts/seed-cli.mjs +3 -6
@@ -1,60 +1,66 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { normalizeError } from "../../../errors/normalize";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
3
  /**
5
- * AdminBundleEditorView — S-SBUNI-4 2026-05-13.
4
+ * AdminBundleEditorView — S-SBUNI-4 2026-05-13, sectionised 2026-08-30.
6
5
  *
7
6
  * Unified create + edit view for categoryType:"bundle" rows. When `bundleId`
8
7
  * is set, loads + edits; when omitted, runs as a "new" form. Delegates to
9
- * /api/admin/bundles. Owns: name + description + bundlePrice +
10
- * static-only product picker + isActive. Dynamic-rule editing is out of scope
11
- * for this session (the API accepts dynamic rules but the form only writes
12
- * static rules; admins editing a pre-existing dynamic bundle see its members
13
- * in the picker but the rule itself stays unchanged until they switch to
14
- * static).
8
+ * /api/admin/bundles (or /api/store/bundles under `scope="store"`).
9
+ *
10
+ * ## The schema now runs
11
+ *
12
+ * It used to declare a seven-field `.passthrough()` stub inside this file,
13
+ * hand it to `<Form schema>`, and validate with two `if` statements instead.
14
+ * So the member-count bounds the picker DISPLAYS — `min 3, max 16` — were red
15
+ * hint text under the control that nothing enforced, and a two-member bundle
16
+ * saved cleanly. The real schema lives in `../../categories/schemas/bundle-form`
17
+ * and is parsed on every change.
18
+ *
19
+ * Static vs dynamic rules are both editable: the rule-type select swaps which
20
+ * member source is on screen, and `visibleValues` makes the payload follow it.
15
21
  */
16
- import React, { useCallback, useEffect, useState } from "react";
22
+ import React from "react";
17
23
  import { useQuery } from "@tanstack/react-query";
18
- import { z } from "zod";
19
- import { Button, Checkbox, ConfirmDeleteModal, Container, Form, Heading, Input, Row, Section, Select, Stack, Text, Textarea, useToast, } from "../../../ui";
20
- import { FieldInput, FormErrorSummary } from "../../../ui/forms";
24
+ import { useApiMutation } from "@mohasinac/appkit/client";
25
+ import { ConfirmDeleteModal, Div, StackedViewShell } from "../../../ui";
26
+ import { FormErrorSummary } from "../../../ui/forms";
27
+ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell";
28
+ import { applyZodIssues } from "../../../ui/forms/apply-zod-issues";
29
+ import { buildSectionsFromSchema, visibleValues } from "../../shell/build-sections";
30
+ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm";
21
31
  import { apiClient } from "../../../http";
22
32
  import { ADMIN_ENDPOINTS, SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
23
33
  import { BundleDynamicRuleEditor } from "../../categories/components/BundleDynamicRuleEditor";
24
34
  import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
25
35
  import { BUNDLE_COPY } from "../../../_internal/shared/features/categories/bundle-copy";
26
- import { BUNDLE_MIN_ITEMS, BUNDLE_MAX_ITEMS, BUNDLE_KIND_SPECIAL, } from "../../../_internal/shared/features/categories/bundle-config";
36
+ import { BUNDLE_KIND_SPECIAL } from "../../../_internal/shared/features/categories/bundle-config";
37
+ import { bundleFormSchema, BUNDLE_RULE_TYPE_OPTIONS, } from "../../categories/schemas/bundle-form";
38
+ const __P = {
39
+ p4: "p-[var(--appkit-space-4)]",
40
+ };
41
+ const __O = {
42
+ yAuto: "overflow-y-auto",
43
+ };
27
44
  const DEFAULT_DYNAMIC_RULE = {
28
45
  type: "dynamic",
29
46
  filter: {},
30
47
  orderBy: "createdAt-desc",
31
48
  limit: 6,
32
49
  };
33
- const RULE_TYPE_OPTIONS = [
34
- { label: BUNDLE_COPY.adminEditor.ruleTypeStatic, value: "static" },
35
- { label: BUNDLE_COPY.adminEditor.ruleTypeDynamic, value: "dynamic" },
36
- ];
50
+ /** No brand is the empty string — `brandSlug: undefined` in the payload. */
51
+ const NO_BRAND_OPTION_VALUE = "";
37
52
  const EMPTY_FORM = {
38
53
  name: "",
39
- description: "",
40
54
  priceRupees: "",
55
+ description: "",
41
56
  ruleType: "static",
42
57
  productIds: [],
43
58
  dynamicRule: DEFAULT_DYNAMIC_RULE,
44
- isActive: true,
45
59
  coverImage: "",
46
60
  brandSlug: "",
61
+ isActive: true,
47
62
  };
48
- const NO_BRAND_OPTION_VALUE = "";
49
- const bundleFormSchema = z.object({
50
- name: z.string().min(1, "Bundle name is required").max(150),
51
- description: z.string().max(2000).optional().or(z.literal("")),
52
- priceRupees: z.string().regex(/^\d+(\.\d{1,2})?$/, "Enter a valid price"),
53
- ruleType: z.enum(["static", "dynamic"]),
54
- productIds: z.array(z.string()),
55
- isActive: z.boolean(),
56
- coverImage: z.string().optional().or(z.literal("")),
57
- }).passthrough();
63
+ /** What the API returns for one bundle — replaces an `as any` at the read. */
58
64
  function bundleToForm(bundle) {
59
65
  if (!bundle)
60
66
  return EMPTY_FORM;
@@ -64,42 +70,24 @@ function bundleToForm(bundle) {
64
70
  const idsFromMirror = bundle.bundleProductIds ?? [];
65
71
  return {
66
72
  name: bundle.name ?? "",
73
+ priceRupees: typeof bundle.bundlePrice === "number" ? String(bundle.bundlePrice) : "",
67
74
  description: bundle.description ?? "",
68
- priceRupees: typeof bundle.bundlePrice === "number"
69
- ? String(bundle.bundlePrice)
70
- : "",
71
75
  ruleType: isDynamic ? "dynamic" : "static",
72
76
  productIds: fromRule.length ? fromRule : idsFromMirror,
73
77
  dynamicRule: isDynamic ? rule : DEFAULT_DYNAMIC_RULE,
74
- isActive: bundle.isActive !== false,
75
78
  coverImage: bundle.display?.coverImage ?? "",
76
79
  brandSlug: bundle.brandSlug ?? "",
80
+ isActive: bundle.isActive !== false,
77
81
  };
78
82
  }
79
- function parsePriceRupees(input) {
80
- const trimmed = input.trim();
81
- if (!trimmed)
82
- return null;
83
- const n = Number(trimmed);
84
- if (!Number.isFinite(n) || n <= 0)
85
- return null;
86
- return Math.round(n * 100) / 100;
87
- }
88
- export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, scope = "admin", }) {
83
+ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, scope = "admin", embedded, ...rest }) {
89
84
  const isEdit = Boolean(bundleId);
90
85
  const endpoints = React.useMemo(() => scope === "admin"
91
86
  ? { collection: ADMIN_ENDPOINTS.BUNDLES, byId: ADMIN_ENDPOINTS.BUNDLE_BY_ID }
92
87
  : { collection: SELLER_ENDPOINTS.BUNDLES, byId: SELLER_ENDPOINTS.BUNDLE_BY_ID }, [scope]);
93
- const [form, setForm] = useState(EMPTY_FORM);
94
- const [loading, setLoading] = useState(isEdit);
95
- const [saving, setSaving] = useState(false);
96
- const [deleting, setDeleting] = useState(false);
97
- const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
98
- const [apiError, setApiError] = useState(null);
99
- const formHelpersRef = React.useRef(null);
100
- const setFieldError = (path, message) => formHelpersRef.current?.setFieldError(path, message);
101
- const clearErrors = () => formHelpersRef.current?.clearErrors();
102
- const { showToast } = useToast();
88
+ const [form, setForm] = React.useState(EMPTY_FORM);
89
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
90
+ const patch = (partial) => setForm((prev) => Object.assign({}, prev, partial));
103
91
  const brandsQuery = useQuery({
104
92
  queryKey: ["admin", "brands", "picker"],
105
93
  queryFn: async () => {
@@ -107,79 +95,53 @@ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, scope = "a
107
95
  return res?.data?.items ?? [];
108
96
  },
109
97
  });
110
- const brandOptions = [
98
+ const brandOptions = React.useMemo(() => [
111
99
  { label: "No specific brand", value: NO_BRAND_OPTION_VALUE },
112
- ...(brandsQuery.data ?? []).map((b) => ({ label: b.name, value: b.slug ?? b.id })),
113
- ];
114
- // Load existing bundle on mount when editing
115
- useEffect(() => {
116
- if (!bundleId)
117
- return;
118
- let cancelled = false;
119
- setLoading(true);
120
- apiClient
121
- .get(endpoints.byId(encodeURIComponent(bundleId)))
122
- .then((res) => {
123
- if (cancelled)
124
- return;
125
- const json = res;
126
- const doc = json?.data ?? null;
127
- setForm(bundleToForm(doc));
128
- })
129
- .catch((err) => {
130
- if (cancelled)
131
- return;
132
- setApiError(err instanceof Error
133
- ? err.message
134
- : BUNDLE_COPY.adminEditor.errors.loadFailed);
135
- })
136
- .finally(() => {
137
- if (!cancelled)
138
- setLoading(false);
139
- });
140
- return () => {
141
- cancelled = true;
142
- };
143
- }, [bundleId, endpoints]);
144
- const handleSave = useCallback(async () => {
145
- // Save is a type="button" onClick — <Form>'s native submit never fires,
146
- // so the error summary has to be unhidden explicitly.
147
- formHelpersRef.current?.markSubmitAttempted();
148
- clearErrors();
149
- setApiError(null);
150
- const price = parsePriceRupees(form.priceRupees);
151
- let hasError = false;
152
- if (!form.name.trim()) {
153
- setFieldError("name", BUNDLE_COPY.adminEditor.errors.nameRequired);
154
- hasError = true;
155
- }
156
- if (price === null) {
157
- setFieldError("price", BUNDLE_COPY.adminEditor.errors.priceInvalid);
158
- hasError = true;
159
- }
160
- if (hasError)
100
+ ...(brandsQuery.data ?? []).map((b) => ({
101
+ label: b.name,
102
+ value: b.slug ?? b.id,
103
+ })),
104
+ ], [brandsQuery.data]);
105
+ const bundleQuery = useQuery({
106
+ queryKey: ["bundle", scope, bundleId],
107
+ queryFn: async () => {
108
+ const res = (await apiClient.get(endpoints.byId(encodeURIComponent(bundleId))));
109
+ return res?.data ?? null;
110
+ },
111
+ enabled: isEdit,
112
+ });
113
+ React.useEffect(() => {
114
+ if (bundleQuery.data === undefined)
161
115
  return;
162
- setSaving(true);
163
- try {
164
- // SB-UNI-5 2026-05-13 — static vs dynamic rule branching.
165
- const bundleQueryRule = form.ruleType === "dynamic"
166
- ? form.dynamicRule
167
- : {
168
- type: "static",
169
- productIds: form.productIds,
170
- };
171
- // For static rules, the mirror equals the picker selection. For dynamic
172
- // rules, the Function resolver writes the mirror; we send an empty list
173
- // on create + leave it untouched on update so we don't clobber the
174
- // resolver's cache.
175
- const bundleProductIds = form.ruleType === "static" ? form.productIds : [];
116
+ setForm(bundleToForm(bundleQuery.data));
117
+ }, [bundleQuery.data]);
118
+ const saveMutation = useApiMutation({
119
+ errorMessage: BUNDLE_COPY.adminEditor.errors.saveFailed,
120
+ successMessage: isEdit ? "Bundle saved." : "Bundle created.",
121
+ mutationFn: async () => {
122
+ /*
123
+ * `visibleValues` decides which member source is sent: the picker is on
124
+ * screen for a static rule and the query editor for a dynamic one, and
125
+ * whichever is hidden is absent from the draft rather than filtered out
126
+ * again here by a second copy of the same condition.
127
+ */
128
+ const draft = visibleValues(bundleFormSchema, form);
129
+ const isStatic = form.ruleType === "static";
130
+ const bundleQueryRule = isStatic
131
+ ? { type: "static", productIds: draft.productIds ?? [] }
132
+ : (draft.dynamicRule ?? DEFAULT_DYNAMIC_RULE);
176
133
  const body = {
177
134
  name: form.name.trim(),
178
135
  description: form.description.trim() || undefined,
179
136
  bundleKind: BUNDLE_KIND_SPECIAL,
180
- bundlePrice: price,
137
+ bundlePrice: Number(form.priceRupees),
181
138
  bundleQueryRule,
182
- bundleProductIds,
139
+ /*
140
+ * For a static rule the mirror equals the picker selection. For a
141
+ * dynamic one the Function resolver owns the mirror; an empty list on
142
+ * create leaves it for the resolver rather than clobbering its cache.
143
+ */
144
+ bundleProductIds: isStatic ? (draft.productIds ?? []) : [],
183
145
  display: form.coverImage.trim()
184
146
  ? { coverImage: form.coverImage.trim() }
185
147
  : undefined,
@@ -187,56 +149,62 @@ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, scope = "a
187
149
  brandSlug: form.brandSlug || undefined,
188
150
  };
189
151
  if (isEdit && bundleId) {
190
- await apiClient.put(endpoints.byId(encodeURIComponent(bundleId)), body);
191
- showToast("Bundle saved.", "success");
192
- onSaved?.(bundleId);
152
+ return apiClient.put(endpoints.byId(encodeURIComponent(bundleId)), body);
193
153
  }
194
- else {
195
- const res = (await apiClient.post(endpoints.collection, body));
196
- const newId = res?.data?.id;
197
- showToast("Bundle created.", "success");
198
- if (newId)
199
- onSaved?.(newId);
200
- }
201
- }
202
- catch (err) {
203
- void normalizeError(err);
204
- const msg = err instanceof Error ? err.message : BUNDLE_COPY.adminEditor.errors.saveFailed;
205
- setApiError(msg);
206
- showToast(msg, "error");
207
- }
208
- finally {
209
- setSaving(false);
210
- }
211
- }, [form, bundleId, isEdit, onSaved, clearErrors, setFieldError, showToast, endpoints]);
212
- const handleDelete = useCallback(async () => {
213
- if (!bundleId)
154
+ return apiClient.post(endpoints.collection, body);
155
+ },
156
+ onSuccess: (res) => {
157
+ const created = res?.data?.id;
158
+ const id = bundleId ?? created;
159
+ if (onSaved && id)
160
+ onSaved(id);
161
+ },
162
+ });
163
+ const deleteMutation = useApiMutation({
164
+ errorMessage: BUNDLE_COPY.adminEditor.errors.deleteFailed,
165
+ successMessage: "Bundle deleted.",
166
+ mutationFn: () => apiClient.delete(endpoints.byId(encodeURIComponent(bundleId))),
167
+ onSuccess: () => {
168
+ if (onDeleted)
169
+ onDeleted();
170
+ },
171
+ });
172
+ const sections = React.useMemo(() => buildSectionsFromSchema(bundleFormSchema, {
173
+ options: { brandSlug: brandOptions, ruleType: BUNDLE_RULE_TYPE_OPTIONS },
174
+ renderers: {
175
+ productIds: ({ values, onChange }) => (_jsx(ProductInlineSelect, { scope: scope, multiple: true, value: values.productIds, onChange: (ids) => onChange({ productIds: ids }), placeholder: "Search and select products\u2026" })),
176
+ dynamicRule: ({ values, onChange }) => (_jsx(BundleDynamicRuleEditor, { value: values.dynamicRule, onChange: (next) => onChange({ dynamicRule: next }) })),
177
+ },
178
+ }), [brandOptions, scope]);
179
+ const nav = useSectionFormNav(sections, form, { scope: "admin:bundle-editor" });
180
+ const { shellCtx, setFieldError, clearErrors } = useFormShellState(bundleFormSchema, {
181
+ sections: nav.sectionMeta,
182
+ onGoToSection: nav.goToSection,
183
+ fieldToSectionIndex: nav.fieldToSectionIndex,
184
+ });
185
+ const onSubmit = () => {
186
+ clearErrors();
187
+ const parsed = bundleFormSchema.safeParse(visibleValues(bundleFormSchema, form));
188
+ if (!parsed.success) {
189
+ applyZodIssues(parsed.error.issues, setFieldError);
214
190
  return;
215
- setDeleting(true);
216
- setApiError(null);
217
- try {
218
- await apiClient.delete(endpoints.byId(encodeURIComponent(bundleId)));
219
- showToast("Bundle deleted.", "success");
220
- setDeleteConfirmOpen(false);
221
- onDeleted?.();
222
- }
223
- catch (err) {
224
- void normalizeError(err);
225
- const msg = err instanceof Error ? err.message : BUNDLE_COPY.adminEditor.errors.deleteFailed;
226
- setApiError(msg);
227
- showToast(msg, "error");
228
191
  }
229
- finally {
230
- setDeleting(false);
231
- }
232
- }, [bundleId, onDeleted, showToast, endpoints]);
233
- if (loading) {
234
- return (_jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "lg", children: _jsx(Text, { children: BUNDLE_COPY.adminEditor.loading }) }) }));
192
+ saveMutation.mutate();
193
+ };
194
+ const isSubmitting = saveMutation.isPending || bundleQuery.isLoading;
195
+ const formSection = (_jsxs(_Fragment, { children: [_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: form, onChange: patch, onSubmit: onSubmit, schema: bundleFormSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isSubmitting, submitLabel: BUNDLE_COPY.adminEditor.saveButton(false, isEdit), destructiveAction: isEdit
196
+ ? {
197
+ label: BUNDLE_COPY.adminEditor.deleteButton(false),
198
+ onClick: () => setDeleteConfirmOpen(true),
199
+ }
200
+ : undefined })] }), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete this bundle?", message: BUNDLE_COPY.adminEditor.deleteConfirm, onConfirm: () => {
201
+ deleteMutation.mutate();
202
+ setDeleteConfirmOpen(false);
203
+ }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
204
+ if (embedded) {
205
+ return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formSection });
235
206
  }
236
- return (_jsx(Form, { schema: bundleFormSchema, onSubmit: (e) => e.preventDefault(), children: (helpers) => {
237
- formHelpersRef.current = helpers;
238
- return (_jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "lg", children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "between", wrap: true, children: [_jsx(Heading, { level: 1, size: "2xl", weight: "semibold", color: "primary", children: isEdit
239
- ? BUNDLE_COPY.adminEditorTitleEdit
240
- : BUNDLE_COPY.adminEditorTitleNew }), isEdit && (_jsx(Button, { variant: "danger", onClick: () => setDeleteConfirmOpen(true), disabled: deleting, children: BUNDLE_COPY.adminEditor.deleteButton(deleting) }))] }), _jsx(ConfirmDeleteModal, { isOpen: deleteConfirmOpen, onClose: () => setDeleteConfirmOpen(false), onConfirm: handleDelete, title: "Delete this bundle?", message: BUNDLE_COPY.adminEditor.deleteConfirm, isDeleting: deleting }), apiError && (_jsx(Text, { color: "danger", role: "alert", children: apiError })), _jsxs(Stack, { gap: "md", children: [_jsx(FieldInput, { name: "name", label: BUNDLE_COPY.adminEditor.fields.nameLabel, value: form.name, onChange: (v) => setForm((f) => ({ ...f, name: v })), placeholder: BUNDLE_COPY.adminEditor.fields.namePlaceholder, disabled: saving, required: true }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.fields.descriptionLabel }), _jsx(Textarea, { value: form.description, onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })), placeholder: BUNDLE_COPY.adminEditor.fields.descriptionPlaceholder, rows: 4, disabled: saving })] }), _jsx(Stack, { gap: "xs", children: _jsx(FieldInput, { name: "price", label: BUNDLE_COPY.adminEditor.fields.priceLabel, type: "number", inputMode: "decimal", min: 1, step: 1, value: form.priceRupees, onChange: (v) => setForm((f) => ({ ...f, priceRupees: v })), placeholder: BUNDLE_COPY.adminEditor.fields.pricePlaceholder, disabled: saving, required: true }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.fields.coverImageLabel }), _jsx(Input, { type: "url", value: form.coverImage, onChange: (e) => setForm((f) => ({ ...f, coverImage: e.target.value })), placeholder: "https://\u2026", disabled: saving })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Brand" }), _jsx(Select, { options: brandOptions, value: form.brandSlug, onValueChange: (next) => setForm((f) => ({ ...f, brandSlug: next })), disabled: saving || brandsQuery.isLoading, "aria-label": "Brand" })] }), _jsx(Checkbox, { checked: form.isActive, onChange: (e) => setForm((f) => ({ ...f, isActive: e.target.checked })), disabled: saving, label: BUNDLE_COPY.adminEditor.fields.activeLabel }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.ruleTypeLabel }), _jsx(Select, { options: RULE_TYPE_OPTIONS, value: form.ruleType, onValueChange: (next) => setForm((f) => ({ ...f, ruleType: next })), disabled: saving, "aria-label": BUNDLE_COPY.adminEditor.ruleTypeLabel })] }), form.ruleType === "static" ? (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: [BUNDLE_COPY.adminEditor.ruleTypeStatic, " products (", BUNDLE_MIN_ITEMS, "\u2013", BUNDLE_MAX_ITEMS, ")"] }), _jsx(ProductInlineSelect, { scope: scope, multiple: true, value: form.productIds, onChange: (ids) => setForm((f) => ({ ...f, productIds: ids })), disabled: saving, placeholder: "Search and select products\u2026" }), form.productIds.length > 0 && form.productIds.length < BUNDLE_MIN_ITEMS && (_jsxs(Text, { size: "xs", color: "danger", children: ["Select at least ", BUNDLE_MIN_ITEMS, " products (currently ", form.productIds.length, ")."] })), form.productIds.length > BUNDLE_MAX_ITEMS && (_jsxs(Text, { size: "xs", color: "danger", children: ["Maximum ", BUNDLE_MAX_ITEMS, " products allowed (currently ", form.productIds.length, ")."] }))] })) : (_jsx(BundleDynamicRuleEditor, { value: form.dynamicRule, onChange: (next) => setForm((f) => ({ ...f, dynamicRule: next })), disabled: saving }))] }), _jsx(FormErrorSummary, {}), _jsx(Row, { gap: "sm", align: "center", justify: "end", children: _jsx(Button, { variant: "primary", onClick: handleSave, disabled: saving, "aria-busy": saving, children: BUNDLE_COPY.adminEditor.saveButton(saving, isEdit) }) })] }) }) }));
241
- } }));
207
+ return (_jsx(StackedViewShell, { portal: scope === "store" ? "seller" : "admin", ...rest, title: isEdit
208
+ ? BUNDLE_COPY.adminEditorTitleEdit
209
+ : BUNDLE_COPY.adminEditorTitleNew, sections: [formSection] }));
242
210
  }
@@ -1,21 +1,48 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Row } from "@mohasinac/appkit/ui";
4
3
  import { useApiMutation } from "@mohasinac/appkit/client";
5
4
  import React from "react";
6
5
  import { useQuery } from "@tanstack/react-query";
7
6
  import { z } from "zod";
8
- import { Button, Card, CardBody, ConfirmDeleteModal, Div, Form, Input, PaginatedSelect, Stack, StackedViewShell, Text, Toggle, useToast, Show, StickyToolbar } from "../../../ui";
7
+ import { Card, CardBody, ConfirmDeleteModal, Div, Input, PaginatedSelect, Stack, StackedViewShell, Text, Toggle, useToast, Show, StickyToolbar } from "../../../ui";
9
8
  import { apiClient } from "../../../http";
10
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
10
  import { CategoryQuickCreateForm } from "./CategoryQuickCreateForm";
12
11
  import { FieldInput, FormErrorSummary, FormShellContext, useFormShellState } from "../../../ui/forms";
12
+ import { SectionForm, useSectionFormNav } from "../../shell";
13
+ /**
14
+ * Covers every field the form actually edits.
15
+ *
16
+ * It previously declared four of seven and carried `.passthrough()`, so
17
+ * `description`, `order` and `showInMenu` were unvalidated — and `order` is
18
+ * typed by hand into a text input and sent through `Number()`.
19
+ */
13
20
  const categoryFormSchema = z.object({
14
21
  name: z.string().min(1, "Category name is required").max(120),
15
- slug: z.string().regex(/^[a-z0-9-]+$/, "Lowercase letters, digits and hyphens only").optional().or(z.literal("")),
16
- parentId: z.string().nullable().optional(),
22
+ slug: z
23
+ .string()
24
+ .regex(/^[a-z0-9-]+$/, "Lowercase letters, digits and hyphens only")
25
+ .optional()
26
+ .or(z.literal("")),
27
+ description: z.string().max(500, "Keep the description under 500 characters").optional().or(z.literal("")),
28
+ parentId: z.string().optional().or(z.literal("")),
29
+ order: z
30
+ .string()
31
+ .regex(/^\d*$/, "Display order must be a whole number")
32
+ .optional()
33
+ .or(z.literal("")),
17
34
  isActive: z.boolean().optional(),
18
- }).passthrough();
35
+ showInMenu: z.boolean().optional(),
36
+ });
37
+ const EMPTY_VALUES = {
38
+ name: "",
39
+ slug: "",
40
+ description: "",
41
+ parentId: "",
42
+ order: "",
43
+ isActive: true,
44
+ showInMenu: true,
45
+ };
19
46
  const __P = {
20
47
  p4: "p-[var(--appkit-space-4)]",
21
48
  };
@@ -45,16 +72,23 @@ function toCategorySlug(str) {
45
72
  export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedded, ...rest }) {
46
73
  const isEdit = Boolean(categoryId);
47
74
  const [deleteOpen, setDeleteOpen] = React.useState(false);
48
- const { shellCtx } = useFormShellState(categoryFormSchema);
49
- const [name, setName] = React.useState("");
50
- const [slug, setSlug] = React.useState("");
75
+ const [values, setValues] = React.useState(EMPTY_VALUES);
76
+ /* UI state, not a form value — whether the user has taken the slug over. */
51
77
  const [slugManual, setSlugManual] = React.useState(false);
52
- const [description, setDescription] = React.useState("");
53
- const [parentId, setParentId] = React.useState("");
54
- const [order, setOrder] = React.useState("");
55
- const [isActive, setIsActive] = React.useState(true);
56
- const [showInMenu, setShowInMenu] = React.useState(true);
57
78
  const { showToast } = useToast();
79
+ const handleChange = React.useCallback((partial) => {
80
+ setValues((prev) => {
81
+ // Object.assign, not a spread: spreading a `Partial<T>` widens every
82
+ // property to `| undefined`, which no longer satisfies the index
83
+ // signature. The intersection Object.assign returns does.
84
+ const next = Object.assign({}, prev, partial);
85
+ // Slug tracks the name until the user edits it, and `slugManual` is what
86
+ // stops it snapping back afterwards.
87
+ if (partial.name !== undefined && !slugManual)
88
+ next.slug = toCategorySlug(String(partial.name));
89
+ return next;
90
+ });
91
+ }, [slugManual]);
58
92
  const categoryQuery = useQuery({
59
93
  queryKey: ["admin", "category", categoryId],
60
94
  queryFn: async () => {
@@ -67,31 +101,31 @@ export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedd
67
101
  const cat = categoryQuery.data;
68
102
  if (!cat)
69
103
  return;
70
- setName(cat.name ?? "");
71
- setSlug(cat.slug ?? "");
72
104
  setSlugManual(true);
73
- setDescription(cat.description ?? "");
74
- setParentId(cat.parentId ?? cat.parentIds?.[cat.parentIds?.length - 1] ?? "");
75
- setOrder(cat.order !== undefined ? String(cat.order) : "");
76
- setIsActive(cat.isActive ?? true);
77
- setShowInMenu(cat.display?.showInMenu ?? true);
105
+ setValues({
106
+ name: cat.name ?? "",
107
+ slug: cat.slug ?? "",
108
+ description: cat.description ?? "",
109
+ parentId: cat.parentId ??
110
+ cat.parentIds?.[cat.parentIds?.length - 1] ??
111
+ "",
112
+ order: cat.order !== undefined ? String(cat.order) : "",
113
+ isActive: cat.isActive ?? true,
114
+ showInMenu: cat.display?.showInMenu ?? true,
115
+ });
78
116
  }, [categoryQuery.data]);
79
- const handleNameChange = (value) => {
80
- setName(value);
81
- if (!slugManual)
82
- setSlug(toCategorySlug(value));
83
- };
84
117
  const saveMutation = useApiMutation({
85
118
  errorMessage: "Failed to save category.",
86
119
  mutationFn: async () => {
120
+ const name = String(values.name ?? "");
87
121
  const payload = {
88
122
  name,
89
- slug: slug || toCategorySlug(name),
90
- description: description || undefined,
91
- parentId: parentId || undefined,
92
- order: order !== "" ? Number(order) : undefined,
93
- isActive,
94
- display: { showInMenu },
123
+ slug: String(values.slug ?? "") || toCategorySlug(name),
124
+ description: String(values.description ?? "") || undefined,
125
+ parentId: String(values.parentId ?? "") || undefined,
126
+ order: values.order !== "" && values.order !== undefined ? Number(values.order) : undefined,
127
+ isActive: values.isActive ?? true,
128
+ display: { showInMenu: values.showInMenu ?? true },
95
129
  };
96
130
  if (isEdit) {
97
131
  return apiClient.put(ADMIN_ENDPOINTS.CATEGORY_BY_ID(categoryId), payload);
@@ -115,18 +149,61 @@ export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedd
115
149
  },
116
150
  });
117
151
  const isSubmitting = saveMutation.isPending || categoryQuery.isLoading;
118
- const actionSidebar = (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Card, { variant: "outlined", padding: "md", spacing: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: isEdit ? (isActive ? "Active" : "Inactive") : "New" }), _jsx(FormErrorSummary, {}), _jsx(Button, { type: "submit", form: "category-editor-form", className: "w-full", isLoading: isSubmitting, disabled: !name || isSubmitting, children: isEdit ? "Save changes" : "Create category" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", className: "w-full", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete category" }))] }) }));
119
- const formContent = (_jsx(Form, { id: "category-editor-form", schema: categoryFormSchema, shellCtx: shellCtx, onSubmit: (e) => e.preventDefault(), spacing: "lg", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "tracking-widest mb-4", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Identity" }), _jsxs(Stack, { gap: "md", children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(FieldInput, { name: "name", label: "Category name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Toys & Games" }), _jsx(Input, { label: "Slug", value: slug, onChange: (e) => {
120
- setSlug(e.target.value);
121
- setSlugManual(true);
122
- }, placeholder: "toys-and-games", helperText: "Auto-generated from name. Used in URLs." })] }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief description of the category" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Parent category" }), _jsx(PaginatedSelect, { value: parentId || null, onChange: (v) => setParentId(v ?? ""), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026 (leave empty for root)", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: "Parent category", createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, n) => { setParentId(id); onCreated({ value: id, label: n }); }, onCancel: onCancel })) }), _jsx(Text, { size: "xs", color: "muted", children: "Leave empty to create a root category." })] })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(Text, { className: "tracking-widest mb-4", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Display" }), _jsxs(Stack, { gap: "md", children: [_jsx(Input, { label: "Display order", value: order, onChange: (e) => setOrder(e.target.value), type: "number", min: 0, placeholder: "0" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Show in menu", checked: showInMenu, onChange: setShowInMenu })] })] }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", className: "lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !name || isSubmitting, onClick: () => {
123
- clearErrors();
124
- if (!name.trim()) {
125
- setFieldError("name", "Category name is required");
126
- return;
127
- }
128
- saveMutation.mutate();
129
- }, children: isEdit ? "Save changes" : "Create category" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete category" }))] })] })) }, "cat-form"));
152
+ /*
153
+ * The sections. Order comes from `required` first, then the group band
154
+ * (`basics` -> required, `visibility` -> visibility), so "mandatory first,
155
+ * least important last" needs no per-form sectionOrder.
156
+ *
157
+ * `fields` is what makes the error summary's jump links and the per-section
158
+ * error badge work — it is the map from a Zod path back to the panel holding
159
+ * that control.
160
+ */
161
+ const sections = React.useMemo(() => [
162
+ {
163
+ id: "basics",
164
+ label: "Identity",
165
+ required: true,
166
+ fields: ["name", "slug", "description", "parentId"],
167
+ render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(FieldInput, { name: "name", label: "Category name", value: String(v.name ?? ""), onChange: (val) => onChange({ name: val }), required: true, placeholder: "e.g. Toys & Games", error: errors.name }), _jsx(Input, { label: "Slug", value: String(v.slug ?? ""), onChange: (e) => {
168
+ setSlugManual(true);
169
+ onChange({ slug: e.target.value });
170
+ }, placeholder: "toys-and-games", helperText: "Auto-generated from the name until you edit it. Used in URLs.", error: errors.slug })] }), _jsx(Input, { label: "Description", value: String(v.description ?? ""), onChange: (e) => onChange({ description: e.target.value }), placeholder: "Brief description of the category", error: errors.description }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Parent category" }), _jsx(PaginatedSelect, { value: String(v.parentId ?? "") || null, onChange: (val) => onChange({ parentId: val ?? "" }), loadOptions: loadCategoryOptions, placeholder: "Search categories\u2026 (leave empty for root)", searchPlaceholder: "Type category name\u2026", noResultsText: "No categories found", ariaLabel: "Parent category", createLabel: "Category", renderCreateForm: ({ onCreated, onCancel }) => (_jsx(CategoryQuickCreateForm, { onSaved: (id, n) => {
171
+ onChange({ parentId: id });
172
+ onCreated({ value: id, label: n });
173
+ }, onCancel: onCancel })) }), _jsx(Text, { size: "xs", color: "muted", children: "Leave empty to create a root category." })] })] })),
174
+ },
175
+ {
176
+ id: "visibility",
177
+ label: "Display",
178
+ fields: ["order", "isActive", "showInMenu"],
179
+ render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Input, { label: "Display order", value: String(v.order ?? ""), onChange: (e) => onChange({ order: e.target.value }), type: "number", min: 0, placeholder: "0", error: errors.order }), _jsx(Toggle, { label: "Active", checked: v.isActive ?? true, onChange: (checked) => onChange({ isActive: checked }) }), _jsx(Toggle, { label: "Show in menu", checked: v.showInMenu ?? true, onChange: (checked) => onChange({ showInMenu: checked }) })] })),
180
+ },
181
+ ], []);
182
+ const nav = useSectionFormNav(sections, values);
183
+ const { shellCtx } = useFormShellState(categoryFormSchema, {
184
+ sections: nav.sectionMeta,
185
+ onGoToSection: nav.goToSection,
186
+ fieldToSectionIndex: nav.fieldToSectionIndex,
187
+ });
188
+ /*
189
+ * The sidebar is STATUS ONLY now.
190
+ *
191
+ * It used to carry its own Save and Delete, and the Save was dead: it was
192
+ * `type="submit"` against a `<Form onSubmit={(e) => e.preventDefault()}>`
193
+ * with no onClick of its own, while the only call to `saveMutation.mutate()`
194
+ * lived in a second, `lg:hidden` button row. The two were mutually exclusive
195
+ * by breakpoint, so on any desktop viewport the admin category editor could
196
+ * not save at all. One action list rendered twice is what makes that
197
+ * unrepresentable.
198
+ */
199
+ const actionSidebar = (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Card, { variant: "outlined", padding: "md", spacing: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: isEdit ? (values.isActive ? "Active" : "Inactive") : "New" }), _jsx(FormErrorSummary, {})] }) }));
200
+ const formContent = (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(SectionForm, { sections: sections, values: values, onChange: handleChange, onSubmit: () => saveMutation.mutate(), schema: categoryFormSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isSubmitting, submitLabel: isEdit ? "Save changes" : "Create category", destructiveAction: isEdit
201
+ ? {
202
+ label: "Delete category",
203
+ onClick: () => setDeleteOpen(true),
204
+ disabled: deleteMutation.isPending,
205
+ }
206
+ : undefined }) }));
130
207
  if (embedded) {
131
208
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
132
209
  }