@mohasinac/appkit 4.30.18 → 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 (557) hide show
  1. package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +5 -2
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +3 -3
  3. package/dist/_internal/client/features/layout/useSidebarSearch.d.ts +21 -5
  4. package/dist/_internal/client/features/layout/useSidebarSearch.js +27 -6
  5. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +23 -3
  6. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +106 -86
  7. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +16 -5
  8. package/dist/_internal/client/features/lottery/LotteryPullForm.js +65 -40
  9. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +5 -2
  10. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +11 -3
  11. package/dist/_internal/client/hand-mode/HandModeProvider.js +2 -0
  12. package/dist/_internal/server/features/auctions/actions.js +14 -3
  13. package/dist/_internal/server/features/auctions/service.js +1 -1
  14. package/dist/_internal/server/features/auth/permissions.d.ts +2 -0
  15. package/dist/_internal/server/features/auth/permissions.js +3 -2
  16. package/dist/_internal/server/features/blog/data.js +2 -2
  17. package/dist/_internal/server/features/blog/service.js +3 -3
  18. package/dist/_internal/server/features/brands/data.js +1 -1
  19. package/dist/_internal/server/features/brands/service.js +3 -3
  20. package/dist/_internal/server/features/bundles/actions.js +6 -1
  21. package/dist/_internal/server/features/bundles/data.js +21 -4
  22. package/dist/_internal/server/features/cart/data.js +3 -1
  23. package/dist/_internal/server/features/cart/service.js +13 -2
  24. package/dist/_internal/server/features/categories/data.js +26 -6
  25. package/dist/_internal/server/features/classified/index.d.ts +0 -1
  26. package/dist/_internal/server/features/classified/index.js +0 -1
  27. package/dist/_internal/server/features/digital-code/index.d.ts +0 -1
  28. package/dist/_internal/server/features/digital-code/index.js +0 -1
  29. package/dist/_internal/server/features/events/actions.js +17 -4
  30. package/dist/_internal/server/features/events/data.js +1 -1
  31. package/dist/_internal/server/features/events/service.js +1 -1
  32. package/dist/_internal/server/features/grouped/data.js +7 -2
  33. package/dist/_internal/server/features/item-requests/data.js +1 -1
  34. package/dist/_internal/server/features/live/index.d.ts +0 -1
  35. package/dist/_internal/server/features/live/index.js +0 -1
  36. package/dist/_internal/server/features/lottery/actions.js +2 -2
  37. package/dist/_internal/server/features/lottery/data.js +1 -1
  38. package/dist/_internal/server/features/orders/actions.js +7 -7
  39. package/dist/_internal/server/features/orders/data.d.ts +1 -1
  40. package/dist/_internal/server/features/orders/data.js +14 -2
  41. package/dist/_internal/server/features/orders/service.js +1 -1
  42. package/dist/_internal/server/features/pre-orders/service.js +1 -1
  43. package/dist/_internal/server/features/prize-draws/actions.js +1 -1
  44. package/dist/_internal/server/features/products/data.js +21 -6
  45. package/dist/_internal/server/features/products/list-public.d.ts +9 -0
  46. package/dist/_internal/server/features/products/list-public.js +95 -7
  47. package/dist/_internal/server/features/products/service.js +1 -1
  48. package/dist/_internal/server/features/promotions/actions.js +1 -1
  49. package/dist/_internal/server/features/promotions/data.js +1 -1
  50. package/dist/_internal/server/features/promotions/service.js +1 -1
  51. package/dist/_internal/server/features/reviews/data.d.ts +1 -1
  52. package/dist/_internal/server/features/reviews/data.js +6 -1
  53. package/dist/_internal/server/features/reviews/og-data.js +1 -1
  54. package/dist/_internal/server/features/shared/listing-data-factory.js +7 -2
  55. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  56. package/dist/_internal/server/features/site-settings/adapters.js +8 -5
  57. package/dist/_internal/server/features/stores/data.js +1 -1
  58. package/dist/_internal/server/features/sublisting-categories/data.js +1 -1
  59. package/dist/_internal/server/features/tester/visibility.d.ts +2 -1
  60. package/dist/_internal/server/features/tester/visibility.js +2 -2
  61. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  62. package/dist/_internal/server/functions/scheduled.js +27 -1
  63. package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -1
  64. package/dist/_internal/server/jobs/core/hardBanCascade.js +4 -0
  65. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  66. package/dist/_internal/server/jobs/core/index.js +2 -0
  67. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +7 -15
  68. package/dist/_internal/server/jobs/core/pageViewPrune.d.ts +10 -0
  69. package/dist/_internal/server/jobs/core/pageViewPrune.js +44 -0
  70. package/dist/_internal/server/jobs/core/pageViewRollup.d.ts +2 -0
  71. package/dist/_internal/server/jobs/core/pageViewRollup.js +87 -0
  72. package/dist/_internal/server/jobs/core/revenueRollup.js +7 -1
  73. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  75. package/dist/_internal/server/jobs/handlers/pageViewPrune.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/pageViewPrune.js +2 -0
  77. package/dist/_internal/server/jobs/handlers/pageViewRollup.d.ts +2 -0
  78. package/dist/_internal/server/jobs/handlers/pageViewRollup.js +2 -0
  79. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  80. package/dist/_internal/shared/actions/action-registry.js +69 -22
  81. package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -4
  82. package/dist/_internal/shared/checkout/rules/classified.rule.js +7 -2
  83. package/dist/_internal/shared/features/layout/matchesNavQuery.d.ts +14 -0
  84. package/dist/_internal/shared/features/layout/matchesNavQuery.js +72 -0
  85. package/dist/_internal/shared/features/layout/types.d.ts +37 -1
  86. package/dist/_internal/shared/features/layout/types.js +19 -1
  87. package/dist/_internal/shared/features/notifications/action-url.d.ts +9 -1
  88. package/dist/_internal/shared/features/notifications/action-url.js +47 -0
  89. package/dist/_internal/shared/features/offers/config.d.ts +65 -0
  90. package/dist/_internal/shared/features/offers/config.js +73 -2
  91. package/dist/_internal/shared/features/orders/schema.d.ts +19 -1
  92. package/dist/_internal/shared/features/orders/schema.js +22 -2
  93. package/dist/_internal/shared/features/site-settings/types.d.ts +12 -3
  94. package/dist/_internal/shared/listing-types/capabilities.d.ts +8 -2
  95. package/dist/_internal/shared/listing-types/capabilities.js +6 -1
  96. package/dist/_internal/shared/listing-types/classified/ctas.js +6 -1
  97. package/dist/_internal/shared/listing-types/feature-flags.d.ts +10 -5
  98. package/dist/_internal/shared/listing-types/feature-flags.js +11 -6
  99. package/dist/client/api/useApiQuery.js +17 -6
  100. package/dist/client.d.ts +38 -20
  101. package/dist/client.js +39 -14
  102. package/dist/configs/next.js +10 -2
  103. package/dist/constants/api-endpoints.d.ts +18 -54
  104. package/dist/constants/api-endpoints.js +6 -26
  105. package/dist/constants/field-names.d.ts +6 -24
  106. package/dist/constants/field-names.js +10 -27
  107. package/dist/constants/geo/countries.d.ts +50 -0
  108. package/dist/constants/geo/countries.js +129 -0
  109. package/dist/constants/geo/subdivisions.d.ts +13 -0
  110. package/dist/constants/geo/subdivisions.js +104 -0
  111. package/dist/constants/index.d.ts +2 -2
  112. package/dist/constants/index.js +2 -2
  113. package/dist/constants/table-keys.d.ts +24 -0
  114. package/dist/constants/table-keys.js +28 -0
  115. package/dist/contracts/client-realtime.d.ts +6 -0
  116. package/dist/contracts/extend.d.ts +0 -9
  117. package/dist/contracts/index.d.ts +1 -1
  118. package/dist/contracts/realtime-session.d.ts +12 -12
  119. package/dist/contracts/realtime-session.js +79 -77
  120. package/dist/core/browser.d.ts +1 -1
  121. package/dist/core/browser.js +1 -1
  122. package/dist/core/index.d.ts +1 -1
  123. package/dist/core/index.js +1 -1
  124. package/dist/core/site-config.d.ts +0 -3
  125. package/dist/core/site-config.js +0 -3
  126. package/dist/errors/index.d.ts +1 -1
  127. package/dist/errors/index.js +1 -1
  128. package/dist/errors/messages.d.ts +0 -8
  129. package/dist/errors/messages.js +0 -9
  130. package/dist/features/about/components/DeveloperView.js +7 -2
  131. package/dist/features/about/components/PolicyPageView.js +9 -1
  132. package/dist/features/about/components/PublicProfileView.js +23 -5
  133. package/dist/features/about/schemas/index.d.ts +8 -8
  134. package/dist/features/account/components/AddressFilters.js +32 -11
  135. package/dist/features/account/components/AddressForm.d.ts +26 -25
  136. package/dist/features/account/components/AddressForm.js +81 -103
  137. package/dist/features/account/components/AddressesIndexListing.js +8 -4
  138. package/dist/features/account/components/NotificationBell.js +4 -3
  139. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  140. package/dist/features/account/components/UserOffersPanel.js +1 -1
  141. package/dist/features/account/components/UserSidebar.d.ts +3 -4
  142. package/dist/features/account/components/UserSupportView.js +89 -58
  143. package/dist/features/account/components/index.d.ts +1 -7
  144. package/dist/features/account/components/index.js +0 -3
  145. package/dist/features/account/hooks/useAddresses.d.ts +4 -3
  146. package/dist/features/account/hooks/useAddresses.js +4 -6
  147. package/dist/features/account/hooks/useCollapsedSections.d.ts +51 -10
  148. package/dist/features/account/hooks/useCollapsedSections.js +211 -38
  149. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  150. package/dist/features/account/index.d.ts +0 -3
  151. package/dist/features/account/index.js +0 -3
  152. package/dist/features/account/schemas/index.d.ts +0 -92
  153. package/dist/features/account/schemas/index.js +42 -20
  154. package/dist/features/addresses/components/AddressLocationFields.d.ts +13 -0
  155. package/dist/features/addresses/components/AddressLocationFields.js +41 -0
  156. package/dist/features/addresses/hooks/usePostalLookup.d.ts +17 -0
  157. package/dist/features/addresses/hooks/usePostalLookup.js +133 -0
  158. package/dist/features/addresses/schemas/address-form.d.ts +423 -0
  159. package/dist/features/addresses/schemas/address-form.js +212 -0
  160. package/dist/features/addresses/schemas/index.d.ts +1 -0
  161. package/dist/features/addresses/schemas/index.js +8 -0
  162. package/dist/features/admin/actions/admin-read-actions.d.ts +1 -1
  163. package/dist/features/admin/actions/index.d.ts +0 -1
  164. package/dist/features/admin/actions/index.js +0 -1
  165. package/dist/features/admin/actions/notification-actions.js +1 -41
  166. package/dist/features/admin/components/AdminActionIndexView.d.ts +32 -0
  167. package/dist/features/admin/components/AdminActionIndexView.js +128 -0
  168. package/dist/features/admin/components/AdminAdEditorView.js +159 -163
  169. package/dist/features/admin/components/AdminAddressEditorView.js +97 -55
  170. package/dist/features/admin/components/AdminBrandEditorView.d.ts +15 -0
  171. package/dist/features/admin/components/AdminBrandEditorView.js +113 -77
  172. package/dist/features/admin/components/AdminBundleEditorView.d.ts +19 -9
  173. package/dist/features/admin/components/AdminBundleEditorView.js +136 -168
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +120 -43
  175. package/dist/features/admin/components/AdminCouponEditorView.js +145 -109
  176. package/dist/features/admin/components/AdminEmployeeEditorView.js +125 -56
  177. package/dist/features/admin/components/AdminFaqEditorView.js +112 -58
  178. package/dist/features/admin/components/AdminFeatureEditorView.js +108 -68
  179. package/dist/features/admin/components/AdminFulfillmentView.js +5 -2
  180. package/dist/features/admin/components/AdminMediaView.js +3 -3
  181. package/dist/features/admin/components/AdminNavEditorView.js +69 -44
  182. package/dist/features/admin/components/AdminOrderEditorView.js +47 -10
  183. package/dist/features/admin/components/AdminPrizeDrawsView.js +16 -2
  184. package/dist/features/admin/components/AdminSidebar.d.ts +11 -7
  185. package/dist/features/admin/components/AdminSiteSettingsView.js +37 -37
  186. package/dist/features/admin/components/AdminStoreEditorView.js +75 -52
  187. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +71 -30
  188. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +95 -65
  189. package/dist/features/admin/components/BrandQuickCreateForm.d.ts +10 -0
  190. package/dist/features/admin/components/BrandQuickCreateForm.js +49 -26
  191. package/dist/features/admin/components/CategoryQuickCreateForm.d.ts +11 -0
  192. package/dist/features/admin/components/CategoryQuickCreateForm.js +48 -26
  193. package/dist/features/admin/components/index.d.ts +2 -2
  194. package/dist/features/admin/components/index.js +1 -1
  195. package/dist/features/admin/constants/site-settings-tabs.d.ts +120 -0
  196. package/dist/features/admin/constants/site-settings-tabs.js +152 -0
  197. package/dist/features/admin/index.d.ts +0 -3
  198. package/dist/features/admin/index.js +0 -2
  199. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +35 -0
  200. package/dist/features/admin/repository/analytics-rollup.repository.js +30 -0
  201. package/dist/features/admin/schemas/admin-editor-forms.d.ts +56 -57
  202. package/dist/features/admin/schemas/admin-editor-forms.js +97 -54
  203. package/dist/features/admin/schemas/admin-ops-forms.d.ts +57 -5
  204. package/dist/features/admin/schemas/admin-ops-forms.js +104 -20
  205. package/dist/features/admin/schemas/admin-user-form.js +7 -0
  206. package/dist/features/admin/schemas/firestore.d.ts +48 -95
  207. package/dist/features/admin/schemas/firestore.js +15 -151
  208. package/dist/features/admin/schemas/nav-item-form.js +9 -2
  209. package/dist/features/admin/server.d.ts +0 -4
  210. package/dist/features/admin/server.js +0 -2
  211. package/dist/features/admin/types/index.d.ts +0 -7
  212. package/dist/features/analytics/repository/page-views.repository.d.ts +14 -0
  213. package/dist/features/analytics/repository/page-views.repository.js +24 -0
  214. package/dist/features/analytics/types.d.ts +1 -1
  215. package/dist/features/analytics/types.js +13 -0
  216. package/dist/features/auctions/components/AuctionDetailPageView.js +6 -2
  217. package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -1
  218. package/dist/features/auth/actions/index.d.ts +0 -1
  219. package/dist/features/auth/actions/index.js +0 -1
  220. package/dist/features/auth/actions/profile-actions.js +4 -3
  221. package/dist/features/auth/actions/realtime-token-actions.js +1 -12
  222. package/dist/features/auth/auth-helpers.js +5 -7
  223. package/dist/features/auth/components/ForgotPasswordView.js +1 -1
  224. package/dist/features/auth/components/Guards.js +2 -1
  225. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  226. package/dist/features/auth/components/LoginForm.js +2 -4
  227. package/dist/features/auth/components/RegisterForm.js +62 -19
  228. package/dist/features/auth/components/ResetPasswordView.js +1 -1
  229. package/dist/features/auth/hooks/useAuth.js +3 -0
  230. package/dist/features/auth/permissions/constants.d.ts +27 -1
  231. package/dist/features/auth/role-predicates.d.ts +75 -7
  232. package/dist/features/auth/role-predicates.js +28 -2
  233. package/dist/features/auth/schemas/firestore.d.ts +22 -0
  234. package/dist/features/auth/schemas/index.d.ts +1 -1
  235. package/dist/features/auth/schemas/index.js +44 -8
  236. package/dist/features/auth/types/index.d.ts +14 -1
  237. package/dist/features/blog/api/route.js +10 -2
  238. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  239. package/dist/features/blog/hooks/useBlog.js +2 -2
  240. package/dist/features/blog/repository/blog.repository.js +3 -1
  241. package/dist/features/blog/schemas/index.d.ts +4 -0
  242. package/dist/features/blog/schemas/index.js +2 -0
  243. package/dist/features/blog/types/index.d.ts +1 -1
  244. package/dist/features/cart/actions/cart-actions.js +13 -4
  245. package/dist/features/cart/utils/pending-ops.js +1 -1
  246. package/dist/features/catalogue/components/CatalogueItemEditorView.d.ts +22 -0
  247. package/dist/features/catalogue/components/CatalogueItemEditorView.js +112 -52
  248. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  249. package/dist/features/catalogue/schemas/validation.js +47 -10
  250. package/dist/features/categories/components/BrandDetailPageView.js +28 -12
  251. package/dist/features/categories/components/CategoryDetailPageView.js +37 -20
  252. package/dist/features/categories/repository/categories.repository.js +3 -1
  253. package/dist/features/categories/schemas/bundle-form.d.ts +142 -0
  254. package/dist/features/categories/schemas/bundle-form.js +149 -0
  255. package/dist/features/checkout/actions/checkout-value-otp-actions.js +10 -1
  256. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +1 -1
  257. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +5 -4
  258. package/dist/features/classified/components/ClassifiedDetailPageView.js +47 -18
  259. package/dist/features/consultation/components/ConsultationForm.js +1 -1
  260. package/dist/features/consultation/schemas/index.js +34 -10
  261. package/dist/features/corporate/schemas/index.js +36 -15
  262. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +4 -1
  263. package/dist/features/events/components/EventBanner.js +5 -2
  264. package/dist/features/events/components/EventOfferCard.js +3 -1
  265. package/dist/features/events/components/EventPollWidget.js +1 -1
  266. package/dist/features/events/components/SpinWheelView.js +3 -2
  267. package/dist/features/filters/FilterPanel.js +3 -3
  268. package/dist/features/filters/RangeFilter.d.ts +10 -0
  269. package/dist/features/filters/RangeFilter.js +41 -4
  270. package/dist/features/grouped/components/GroupedListingEditorView.js +2 -1
  271. package/dist/features/history/hooks/useHistory.js +29 -27
  272. package/dist/features/homepage/components/AdSlot.js +3 -1
  273. package/dist/features/homepage/components/GoogleReviewsSection.js +8 -2
  274. package/dist/features/homepage/components/MarketplaceHomepageView.js +58 -15
  275. package/dist/features/homepage/components/SocialFeedSection.js +4 -1
  276. package/dist/features/homepage/hooks/useBlogArticles.js +1 -1
  277. package/dist/features/homepage/lib/live-stats.js +8 -1
  278. package/dist/features/homepage/repository/carousel.repository.js +2 -1
  279. package/dist/features/layout/BottomActions.js +84 -11
  280. package/dist/features/layout/BottomActionsContext.d.ts +1 -6
  281. package/dist/features/layout/BottomActionsContext.js +1 -1
  282. package/dist/features/layout/hooks/useBottomActions.js +2 -2
  283. package/dist/features/layout/hooks/useFormBottomActions.d.ts +12 -1
  284. package/dist/features/layout/hooks/useFormBottomActions.js +12 -6
  285. package/dist/features/live/components/LiveItemDetailPageView.js +4 -1
  286. package/dist/features/lottery/schemas/config-write.d.ts +9 -5
  287. package/dist/features/lottery/schemas/config-write.js +69 -9
  288. package/dist/features/media/finalize.js +3 -1
  289. package/dist/features/media/modals/ImageEditor.js +1 -1
  290. package/dist/features/media/modals/VideoThumbnailSelector.js +5 -2
  291. package/dist/features/media/modals/VideoTrimModal.js +5 -2
  292. package/dist/features/media/upload/MediaUploadField.js +2 -2
  293. package/dist/features/messages/hooks/useConversation.js +18 -4
  294. package/dist/features/messages/hooks/useConversations.js +25 -8
  295. package/dist/features/orders/components/RefundRequestView.js +5 -2
  296. package/dist/features/orders/schemas/buyer-forms.d.ts +6 -3
  297. package/dist/features/orders/schemas/buyer-forms.js +31 -3
  298. package/dist/features/orders/schemas/index.d.ts +2 -2
  299. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -1
  300. package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
  301. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
  302. package/dist/features/products/components/GroupSettingsPanel.js +3 -1
  303. package/dist/features/products/components/MakeOfferButton.d.ts +13 -2
  304. package/dist/features/products/components/MakeOfferButton.js +33 -11
  305. package/dist/features/products/components/PrizeDrawDetailPageView.js +4 -1
  306. package/dist/features/products/components/ProductDetailActions.js +0 -1
  307. package/dist/features/products/components/ProductDetailPageView.d.ts +7 -3
  308. package/dist/features/products/components/ProductDetailPageView.js +16 -7
  309. package/dist/features/products/components/ShareButton.js +3 -1
  310. package/dist/features/products/constants/action-defs.d.ts +1 -2
  311. package/dist/features/products/constants/action-defs.js +40 -43
  312. package/dist/features/products/repository/loadProductFeatures.js +10 -2
  313. package/dist/features/products/repository/products.repository.d.ts +5 -1
  314. package/dist/features/products/repository/products.repository.js +25 -2
  315. package/dist/features/products/schemas/firestore.d.ts +7 -3
  316. package/dist/features/products/schemas/product-features.validators.js +3 -0
  317. package/dist/features/promotions/components/CouponCard.js +11 -8
  318. package/dist/features/reviews/actions/review-actions.js +8 -1
  319. package/dist/features/reviews/components/ReviewDetailPageView.js +13 -3
  320. package/dist/features/reviews/components/ReviewDetailShell.js +3 -1
  321. package/dist/features/scams/actions/scam-actions.d.ts +9 -1
  322. package/dist/features/scams/actions/scam-actions.js +52 -12
  323. package/dist/features/scams/components/SellerTrustBadge.d.ts +1 -1
  324. package/dist/features/scams/components/SellerTrustBadge.js +13 -0
  325. package/dist/features/scams/repository/scammer.repository.js +2 -1
  326. package/dist/features/scams/schemas/firestore.d.ts +15 -1
  327. package/dist/features/scams/schemas/report-form.d.ts +5 -5
  328. package/dist/features/scams/schemas/report-form.js +18 -3
  329. package/dist/features/search/action-index/derive.d.ts +49 -0
  330. package/dist/features/search/action-index/derive.js +121 -0
  331. package/dist/features/search/action-index/repository.d.ts +42 -0
  332. package/dist/features/search/action-index/repository.js +100 -0
  333. package/dist/features/search/action-index/settings-entries.d.ts +41 -0
  334. package/dist/features/search/action-index/settings-entries.js +268 -0
  335. package/dist/features/search/action-index/types.d.ts +81 -0
  336. package/dist/features/search/action-index/types.js +102 -0
  337. package/dist/features/search/action-index/validate-entry.d.ts +34 -0
  338. package/dist/features/search/action-index/validate-entry.js +114 -0
  339. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  340. package/dist/features/search/hooks/useNavSuggestions.js +23 -3
  341. package/dist/features/search/repository/search.repository.js +3 -1
  342. package/dist/features/seller/actions/offer-actions.js +43 -19
  343. package/dist/features/seller/actions/seller-actions.js +29 -5
  344. package/dist/features/seller/components/CategoryChipPicker.d.ts +9 -0
  345. package/dist/features/seller/components/CategoryChipPicker.js +12 -0
  346. package/dist/features/seller/components/FulfillmentView.js +5 -2
  347. package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
  348. package/dist/features/seller/components/PrintCenterView.js +3 -1
  349. package/dist/features/seller/components/SellerAddressesView.js +5 -1
  350. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +83 -43
  351. package/dist/features/seller/components/SellerArtView.d.ts +14 -7
  352. package/dist/features/seller/components/SellerArtView.js +4 -119
  353. package/dist/features/seller/components/SellerBidsView.js +1 -1
  354. package/dist/features/seller/components/SellerClassifiedView.d.ts +14 -7
  355. package/dist/features/seller/components/SellerClassifiedView.js +4 -124
  356. package/dist/features/seller/components/SellerCouponEditorView.d.ts +22 -1
  357. package/dist/features/seller/components/SellerCouponEditorView.js +91 -45
  358. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  359. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +14 -7
  360. package/dist/features/seller/components/SellerDigitalCodesView.js +4 -126
  361. package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -2
  362. package/dist/features/seller/components/SellerListingTypeView.d.ts +66 -0
  363. package/dist/features/seller/components/SellerListingTypeView.js +277 -0
  364. package/dist/features/seller/components/SellerLiveView.d.ts +14 -7
  365. package/dist/features/seller/components/SellerLiveView.js +4 -126
  366. package/dist/features/seller/components/SellerOrdersView.js +3 -3
  367. package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
  368. package/dist/features/seller/components/SellerProductShell.d.ts +5 -0
  369. package/dist/features/seller/components/SellerProductShell.js +15 -5
  370. package/dist/features/seller/components/SellerProductsView.js +24 -10
  371. package/dist/features/seller/components/SellerReviewsView.js +47 -16
  372. package/dist/features/seller/components/SellerShippingConfigsView.js +5 -4
  373. package/dist/features/seller/components/SellerSidebar.d.ts +3 -6
  374. package/dist/features/seller/components/SellerStickersView.d.ts +14 -7
  375. package/dist/features/seller/components/SellerStickersView.js +4 -119
  376. package/dist/features/seller/components/SellerStoreCategoriesView.js +3 -2
  377. package/dist/features/seller/components/SellerSupportView.d.ts +31 -0
  378. package/dist/features/seller/components/SellerSupportView.js +100 -0
  379. package/dist/features/seller/components/index.d.ts +4 -8
  380. package/dist/features/seller/components/index.js +2 -4
  381. package/dist/features/seller/index.d.ts +0 -1
  382. package/dist/features/seller/index.js +0 -1
  383. package/dist/features/seller/schemas/coupon-form.d.ts +214 -0
  384. package/dist/features/seller/schemas/coupon-form.js +155 -23
  385. package/dist/features/seller/schemas/offer-forms.d.ts +16 -7
  386. package/dist/features/seller/schemas/offer-forms.js +28 -12
  387. package/dist/features/shell/FormShell.js +2 -2
  388. package/dist/features/shell/QuickFormDrawer.js +2 -2
  389. package/dist/features/shell/SectionForm.d.ts +19 -1
  390. package/dist/features/shell/SectionForm.js +104 -17
  391. package/dist/features/shell/build-sections.d.ts +23 -0
  392. package/dist/features/shell/build-sections.js +106 -8
  393. package/dist/features/shell/field-ui-meta.d.ts +44 -0
  394. package/dist/features/shell/field-ui-meta.js +14 -7
  395. package/dist/features/shell/form-sections.d.ts +46 -0
  396. package/dist/features/shell/form-sections.js +114 -0
  397. package/dist/features/shipments/components/AdminShipmentEditorView.js +87 -64
  398. package/dist/features/shipments/schemas/validation.d.ts +2 -2
  399. package/dist/features/shipments/schemas/validation.js +44 -13
  400. package/dist/features/store-extensions/schemas/custom-role-form.js +6 -2
  401. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +7 -7
  402. package/dist/features/store-extensions/schemas/payout-method-form.js +14 -0
  403. package/dist/features/stores/actions/store-query-actions.js +3 -2
  404. package/dist/features/stores/api/[storeSlug]/auctions/route.js +3 -1
  405. package/dist/features/stores/api/[storeSlug]/products/route.js +3 -1
  406. package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +12 -3
  408. package/dist/features/support/repository/support.repository.d.ts +21 -0
  409. package/dist/features/support/repository/support.repository.js +63 -0
  410. package/dist/features/support/schemas/firestore.d.ts +27 -2
  411. package/dist/features/support/schemas/firestore.js +2 -0
  412. package/dist/features/support/schemas/ticket-create-form.js +7 -0
  413. package/dist/features/tester/schemas/checklist-item-form.d.ts +43 -0
  414. package/dist/features/tester/schemas/checklist-item-form.js +109 -0
  415. package/dist/features/tester/seed-data/blog-tester-seed-data.js +2 -2
  416. package/dist/features/tester/seed-data/categories-tester-seed-data.js +2 -2
  417. package/dist/features/tester/seed-data/events-tester-seed-data.js +5 -5
  418. package/dist/features/tester/seed-data/orders-tester-seed-data.js +2 -2
  419. package/dist/features/tester/seed-data/products-tester-seed-data.js +48 -49
  420. package/dist/features/tester/seed-data/stores-tester-seed-data.js +3 -3
  421. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +291 -30
  422. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +51 -14
  423. package/dist/features/wishlist/hooks/useWishlistToggle.js +8 -1
  424. package/dist/http/api-handler.js +2 -1
  425. package/dist/index.d.ts +25 -104
  426. package/dist/index.js +38 -143
  427. package/dist/jobs.d.ts +1 -1
  428. package/dist/jobs.js +1 -1
  429. package/dist/monitoring/browser.d.ts +0 -2
  430. package/dist/monitoring/browser.js +12 -1
  431. package/dist/monitoring/index.d.ts +0 -2
  432. package/dist/monitoring/index.js +0 -1
  433. package/dist/next/api/routeHandler.js +9 -4
  434. package/dist/next/index.d.ts +1 -2
  435. package/dist/next/index.js +0 -1
  436. package/dist/next/middleware/index.d.ts +0 -2
  437. package/dist/next/middleware/index.js +0 -1
  438. package/dist/next/routing/route-map.d.ts +8 -7
  439. package/dist/next/routing/route-map.js +10 -3
  440. package/dist/providers/auth-firebase/session.js +3 -1
  441. package/dist/providers/db-firebase/admin-app-lite.js +8 -8
  442. package/dist/providers/db-firebase/admin.js +27 -13
  443. package/dist/providers/db-firebase/browser-stub.d.ts +1 -11
  444. package/dist/providers/db-firebase/browser-stub.js +6 -11
  445. package/dist/providers/db-firebase/index.d.ts +0 -1
  446. package/dist/providers/db-firebase/index.js +0 -1
  447. package/dist/providers/db-firebase/rtdb-paths.d.ts +15 -6
  448. package/dist/providers/db-firebase/rtdb-paths.js +15 -6
  449. package/dist/providers/db-firebase/rtdb-url.d.ts +27 -0
  450. package/dist/providers/db-firebase/rtdb-url.js +50 -0
  451. package/dist/providers/db-firebase/sieve.d.ts +22 -0
  452. package/dist/providers/firebase-client/realtime.d.ts +20 -0
  453. package/dist/providers/firebase-client/realtime.js +34 -1
  454. package/dist/providers.d.ts +1 -1
  455. package/dist/providers.js +1 -1
  456. package/dist/react/hooks/use-action-dispatch.js +8 -2
  457. package/dist/react/hooks/useFormStatePreservation.js +3 -1
  458. package/dist/react/hooks/useListingTypeFlags.d.ts +1 -1
  459. package/dist/react/hooks/useListingTypeFlags.js +1 -1
  460. package/dist/react/hooks/usePendingFilters.js +5 -3
  461. package/dist/react/hooks/usePendingTable.js +15 -3
  462. package/dist/react/hooks/useRealtimeEvent.js +20 -9
  463. package/dist/repositories/index.d.ts +0 -1
  464. package/dist/repositories/index.js +0 -1
  465. package/dist/schemas/registry.d.ts +197 -232
  466. package/dist/schemas/registry.js +5 -6
  467. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  468. package/dist/security/authorization.d.ts +9 -0
  469. package/dist/security/authorization.js +16 -2
  470. package/dist/security/browser.d.ts +1 -3
  471. package/dist/security/browser.js +5 -2
  472. package/dist/security/index.d.ts +1 -3
  473. package/dist/security/index.js +19 -2
  474. package/dist/security/pii-encrypt.js +16 -0
  475. package/dist/security/pii-schemas.d.ts +0 -14
  476. package/dist/security/pii-schemas.js +7 -15
  477. package/dist/seed/_helpers/category-forest.js +2 -2
  478. package/dist/seed/_helpers/media.d.ts +19 -0
  479. package/dist/seed/_helpers/media.js +75 -0
  480. package/dist/seed/blog-posts-seed-data.js +17 -17
  481. package/dist/seed/carousel-slides-seed-data.js +7 -7
  482. package/dist/seed/cart-seed-data.js +13 -13
  483. package/dist/seed/categories-seed-data.js +10 -10
  484. package/dist/seed/events-seed-data.js +24 -24
  485. package/dist/seed/factories/product.factory.js +4 -4
  486. package/dist/seed/factories/store.factory.js +3 -3
  487. package/dist/seed/factories/user.factory.js +2 -2
  488. package/dist/seed/grouped-listings-seed-data.js +11 -11
  489. package/dist/seed/homepage-sections-seed-data.js +2 -2
  490. package/dist/seed/index.d.ts +0 -1
  491. package/dist/seed/index.js +0 -1
  492. package/dist/seed/manifest.d.ts +1 -1
  493. package/dist/seed/manifest.js +0 -5
  494. package/dist/seed/notifications-seed-data.js +20 -1
  495. package/dist/seed/orders-seed-data.js +2 -2
  496. package/dist/seed/products-art-seed-data.js +16 -16
  497. package/dist/seed/products-auctions-seed-data.js +34 -34
  498. package/dist/seed/products-classifieds-seed-data.js +35 -37
  499. package/dist/seed/products-digital-codes-seed-data.js +20 -20
  500. package/dist/seed/products-live-items-seed-data.js +11 -11
  501. package/dist/seed/products-preorders-seed-data.js +13 -13
  502. package/dist/seed/products-prize-draws-seed-data.js +43 -43
  503. package/dist/seed/products-standard-seed-data.js +54 -54
  504. package/dist/seed/products-stickers-seed-data.js +15 -15
  505. package/dist/seed/reviews-seed-data.js +4 -4
  506. package/dist/seed/scammers-seed-data.js +3 -3
  507. package/dist/seed/site-settings-seed-data.js +11 -24
  508. package/dist/seed/store-extensions-seed-data.js +10 -10
  509. package/dist/seed/stores-seed-data.js +11 -11
  510. package/dist/seed/support-tickets-seed-data.d.ts +14 -0
  511. package/dist/seed/support-tickets-seed-data.js +20 -1
  512. package/dist/seed/users-seed-data.js +20 -20
  513. package/dist/server-entry.d.ts +0 -1
  514. package/dist/server-entry.js +0 -1
  515. package/dist/server.d.ts +9 -6
  516. package/dist/server.js +7 -13
  517. package/dist/styles.css +248 -68
  518. package/dist/tailwind-utilities.css +1 -1
  519. package/dist/tokens/themes/default-dark.js +2 -2
  520. package/dist/tokens/themes/default-light.js +2 -2
  521. package/dist/tokens/tokens.css +24 -4
  522. package/dist/ui/components/ActionRow.d.ts +50 -0
  523. package/dist/ui/components/ActionRow.js +53 -0
  524. package/dist/ui/components/ActionRow.style.css +145 -0
  525. package/dist/ui/components/Drawer.d.ts +10 -0
  526. package/dist/ui/components/Drawer.js +2 -1
  527. package/dist/ui/components/FilterDrawer.js +4 -1
  528. package/dist/ui/components/FilterDrawer.style.css +6 -12
  529. package/dist/ui/components/FormActionBar.js +14 -1
  530. package/dist/ui/components/FormActionBar.style.css +12 -11
  531. package/dist/ui/components/ListingLayout.js +8 -1
  532. package/dist/ui/components/ListingLayout.style.css +6 -13
  533. package/dist/ui/components/Modal.d.ts +15 -1
  534. package/dist/ui/components/Modal.js +17 -3
  535. package/dist/ui/components/QuickCreateModal.js +2 -2
  536. package/dist/ui/components/QuickCreateModal.style.css +78 -84
  537. package/dist/ui/components/RecordDetailModal.js +2 -2
  538. package/dist/ui/components/SideDrawer.d.ts +8 -0
  539. package/dist/ui/components/SideDrawer.js +2 -1
  540. package/dist/ui/components/SideDrawer.style.css +6 -3
  541. package/dist/ui/components/SideModal.d.ts +11 -1
  542. package/dist/ui/components/SideModal.js +3 -2
  543. package/dist/ui/components/SideModal.style.css +14 -0
  544. package/dist/ui/components/Typography.style.css +33 -24
  545. package/dist/ui/components/index.style.css +1 -0
  546. package/dist/ui/components/toolbar-collapse.js +1 -2
  547. package/dist/ui/forms/FormShell.style.css +260 -311
  548. package/dist/ui/index.d.ts +2 -0
  549. package/dist/ui/index.js +1 -0
  550. package/dist/utils/index.d.ts +0 -1
  551. package/dist/utils/index.js +0 -1
  552. package/dist/validation/index.d.ts +1 -1
  553. package/dist/validation/index.js +1 -1
  554. package/dist/validation/schemas.d.ts +0 -19
  555. package/dist/validation/schemas.js +8 -19
  556. package/package.json +1 -1
  557. 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
  }