@mohasinac/appkit 4.9.5 → 4.11.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 (395) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
  3. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
  4. package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
  5. package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
  6. package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
  7. package/dist/_internal/server/features/auctions/actions.js +3 -2
  8. package/dist/_internal/server/features/auctions/service.d.ts +4 -3
  9. package/dist/_internal/server/features/auctions/service.js +6 -6
  10. package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
  11. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  12. package/dist/_internal/server/features/brands/actions.js +0 -2
  13. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  14. package/dist/_internal/server/features/brands/og.js +1 -1
  15. package/dist/_internal/server/features/bundles/data.d.ts +9 -0
  16. package/dist/_internal/server/features/bundles/data.js +14 -0
  17. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  18. package/dist/_internal/server/features/bundles/index.js +1 -1
  19. package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
  20. package/dist/_internal/server/features/catalogue/og.js +27 -0
  21. package/dist/_internal/server/features/grouped/data.d.ts +16 -0
  22. package/dist/_internal/server/features/grouped/data.js +50 -1
  23. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  24. package/dist/_internal/server/features/grouped/index.js +1 -1
  25. package/dist/_internal/server/features/orders/actions.js +9 -0
  26. package/dist/_internal/server/features/orders/adapters.js +25 -0
  27. package/dist/_internal/server/features/products/data.d.ts +23 -0
  28. package/dist/_internal/server/features/products/data.js +159 -0
  29. package/dist/_internal/server/features/products/index.d.ts +1 -1
  30. package/dist/_internal/server/features/products/index.js +1 -1
  31. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  32. package/dist/_internal/server/functions/scheduled.js +17 -1
  33. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  34. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  35. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  36. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  37. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  38. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  39. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  40. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  41. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  42. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  43. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  44. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  45. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  46. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  47. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  48. package/dist/_internal/server/notifications/channel-health.js +101 -0
  49. package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
  50. package/dist/_internal/shared/features/auctions/config.js +31 -1
  51. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  52. package/dist/_internal/shared/features/brands/schema.js +0 -1
  53. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  54. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  55. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  56. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
  57. package/dist/_internal/shared/listing-types/art/config.js +1 -1
  58. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
  59. package/dist/_internal/shared/listing-types/classified/config.js +1 -1
  60. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
  61. package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
  62. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
  63. package/dist/_internal/shared/listing-types/live/config.js +1 -1
  64. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
  65. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  66. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
  67. package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
  68. package/dist/client.d.ts +21 -12
  69. package/dist/client.js +14 -7
  70. package/dist/constants/api-endpoints.d.ts +9 -9
  71. package/dist/constants/api-endpoints.js +3 -3
  72. package/dist/contracts/client-auth.d.ts +3 -7
  73. package/dist/contracts/email.d.ts +1 -0
  74. package/dist/features/account/components/AddressForm.js +24 -2
  75. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  76. package/dist/features/account/components/BecomeSellerView.js +1 -1
  77. package/dist/features/account/components/NotificationBell.js +1 -1
  78. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  79. package/dist/features/account/components/UserBidsView.js +47 -0
  80. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  81. package/dist/features/account/components/UserOrdersView.js +54 -4
  82. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  83. package/dist/features/account/components/UserReturnsView.js +37 -3
  84. package/dist/features/account/components/index.d.ts +3 -2
  85. package/dist/features/account/components/index.js +1 -0
  86. package/dist/features/admin/actions/admin-actions.js +22 -0
  87. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  88. package/dist/features/admin/actions/notification-actions.js +4 -3
  89. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  90. package/dist/features/admin/components/AdminAdsView.js +86 -89
  91. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  92. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  93. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  94. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  95. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  96. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  97. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  98. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  99. package/dist/features/admin/components/AdminCartsView.js +34 -4
  100. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  101. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  102. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  103. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  104. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  105. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  106. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  107. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  108. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  109. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  110. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  111. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  112. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -4
  113. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  114. package/dist/features/admin/components/AdminStoresView.js +7 -0
  115. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  116. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  117. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  118. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  119. package/dist/features/admin/components/AdminUsersView.js +1 -1
  120. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  121. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  122. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  123. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  124. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  125. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  126. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  127. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  128. package/dist/features/admin/components/index.d.ts +8 -1
  129. package/dist/features/admin/components/index.js +4 -0
  130. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  131. package/dist/features/admin/constants/filter-tabs.js +27 -0
  132. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  133. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  134. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  135. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  136. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  137. package/dist/features/admin/schemas/firestore.d.ts +21 -0
  138. package/dist/features/admin/schemas/firestore.js +6 -0
  139. package/dist/features/analytics/types.d.ts +1 -1
  140. package/dist/features/analytics/types.js +8 -0
  141. package/dist/features/auctions/actions/bid-actions.js +4 -1
  142. package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
  143. package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
  144. package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
  145. package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
  146. package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
  147. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
  148. package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
  149. package/dist/features/auctions/schemas/index.d.ts +5 -0
  150. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  151. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  152. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  153. package/dist/features/audit-log/schemas/firestore.js +35 -0
  154. package/dist/features/auth/actions/index.d.ts +0 -1
  155. package/dist/features/auth/actions/index.js +0 -1
  156. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  157. package/dist/features/auth/hooks/useAuth.js +21 -37
  158. package/dist/features/auth/permissions/constants.d.ts +1 -1
  159. package/dist/features/auth/permissions/constants.js +2 -0
  160. package/dist/features/blog/schemas/index.d.ts +32 -0
  161. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  162. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  163. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
  164. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
  165. package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
  166. package/dist/features/catalogue/index.d.ts +2 -0
  167. package/dist/features/catalogue/index.js +1 -0
  168. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  169. package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
  170. package/dist/features/categories/components/BundleDetailView.js +3 -2
  171. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  172. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  173. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  174. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  175. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  176. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  177. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  178. package/dist/features/categories/schemas/index.d.ts +24 -3
  179. package/dist/features/categories/schemas/index.js +3 -1
  180. package/dist/features/categories/types/index.d.ts +7 -0
  181. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  182. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  183. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
  184. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
  185. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
  186. package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
  187. package/dist/features/contact/components/ContactForm.js +17 -4
  188. package/dist/features/contact/components/ContactPageView.js +8 -2
  189. package/dist/features/contact/email.d.ts +1 -0
  190. package/dist/features/contact/email.js +3 -0
  191. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
  192. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
  193. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  194. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  195. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  196. package/dist/features/events/components/EventFormDrawer.js +5 -1
  197. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  198. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  199. package/dist/features/events/schemas/index.d.ts +22 -0
  200. package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
  201. package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
  202. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +12 -0
  203. package/dist/features/grouped/repository/grouped-listings.repository.js +34 -0
  204. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  205. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  206. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  207. package/dist/features/layout/BottomActions.js +4 -3
  208. package/dist/features/layout/BottomActionsContext.d.ts +10 -0
  209. package/dist/features/layout/BottomActionsContext.js +5 -1
  210. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  211. package/dist/features/layout/TitleBarLayout.js +3 -3
  212. package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
  213. package/dist/features/layout/hooks/useBottomActions.js +4 -1
  214. package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
  215. package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
  216. package/dist/features/media/MediaImage.js +39 -3
  217. package/dist/features/media/MediaVideo.js +17 -1
  218. package/dist/features/media/types/index.d.ts +10 -0
  219. package/dist/features/media/types/index.js +1 -0
  220. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  221. package/dist/features/media/upload/MediaUploadField.js +11 -7
  222. package/dist/features/media/upload/video-poster.d.ts +2 -0
  223. package/dist/features/media/upload/video-poster.js +16 -1
  224. package/dist/features/media/upload/video-poster.test.js +1 -1
  225. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  226. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  227. package/dist/features/orders/components/index.d.ts +2 -0
  228. package/dist/features/orders/components/index.js +1 -0
  229. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  230. package/dist/features/orders/constants/payment-window.js +25 -0
  231. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  232. package/dist/features/orders/repository/orders.repository.js +56 -0
  233. package/dist/features/orders/schemas/index.d.ts +24 -24
  234. package/dist/features/orders/types/index.d.ts +33 -0
  235. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
  236. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  237. package/dist/features/products/components/BidHistory.js +9 -1
  238. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  239. package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
  240. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  241. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  242. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  243. package/dist/features/products/components/ProductDetailActions.js +1 -1
  244. package/dist/features/products/components/ProductDetailPageView.js +11 -116
  245. package/dist/features/products/components/ProductGrid.js +15 -1
  246. package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
  247. package/dist/features/products/components/RelatedItemsSection.js +17 -0
  248. package/dist/features/products/constants/action-defs.d.ts +1 -0
  249. package/dist/features/products/constants/action-defs.js +1 -0
  250. package/dist/features/products/schemas/index.d.ts +5 -0
  251. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  252. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  253. package/dist/features/search/components/SearchView.d.ts +1 -3
  254. package/dist/features/search/components/SearchView.js +1 -1
  255. package/dist/features/seller/components/QuickProductForm.js +2 -1
  256. package/dist/features/seller/components/SellerBidsView.js +66 -107
  257. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  258. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  259. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  260. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  261. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  262. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  263. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  264. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  265. package/dist/features/seller/components/SellerProductShell.js +90 -37
  266. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  267. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  268. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  269. package/dist/features/seller/components/SellerProductsView.js +131 -189
  270. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  271. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  272. package/dist/features/seller/components/SellerShippingView.js +41 -4
  273. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  274. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  275. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  276. package/dist/features/seller/components/index.d.ts +2 -2
  277. package/dist/features/seller/components/index.js +2 -2
  278. package/dist/features/shell/FormShell.d.ts +24 -4
  279. package/dist/features/shell/FormShell.js +12 -6
  280. package/dist/features/shell/QuickFormDrawer.js +67 -12
  281. package/dist/features/shell/StepForm.d.ts +30 -1
  282. package/dist/features/shell/StepForm.js +47 -4
  283. package/dist/features/shell/index.d.ts +1 -1
  284. package/dist/features/shell/index.js +1 -1
  285. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  286. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  287. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  288. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  289. package/dist/features/tester/components/TesterHubView.js +3 -2
  290. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  291. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  292. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +487 -18
  293. package/dist/index.d.ts +17 -3
  294. package/dist/index.js +23 -5
  295. package/dist/next/routing/route-map.d.ts +13 -0
  296. package/dist/next/routing/route-map.js +12 -0
  297. package/dist/providers/email-resend/provider.js +1 -0
  298. package/dist/providers/firebase-client/auth.d.ts +1 -0
  299. package/dist/providers/firebase-client/auth.js +7 -1
  300. package/dist/repositories/index.d.ts +1 -0
  301. package/dist/repositories/index.js +1 -0
  302. package/dist/schemas/registry.d.ts +61 -25
  303. package/dist/security/pii-mask.js +1 -1
  304. package/dist/seed/bids-seed-data.js +48 -3
  305. package/dist/seed/categories-seed-data.js +69 -0
  306. package/dist/seed/grouped-listings-seed-data.js +167 -0
  307. package/dist/seed/payouts-seed-data.js +4 -4
  308. package/dist/seed/products-art-seed-data.js +188 -5
  309. package/dist/seed/products-auctions-seed-data.js +226 -6
  310. package/dist/seed/products-classifieds-seed-data.js +255 -8
  311. package/dist/seed/products-digital-codes-seed-data.js +258 -19
  312. package/dist/seed/products-live-items-seed-data.js +143 -10
  313. package/dist/seed/products-preorders-seed-data.js +213 -8
  314. package/dist/seed/products-prize-draws-seed-data.js +198 -0
  315. package/dist/seed/products-standard-seed-data.js +234 -4
  316. package/dist/seed/products-stickers-seed-data.js +178 -5
  317. package/dist/seed/site-settings-seed-data.js +23 -30
  318. package/dist/seed/store-extensions-seed-data.js +2 -2
  319. package/dist/server-entry.d.ts +6 -2
  320. package/dist/server-entry.js +24 -3
  321. package/dist/server.d.ts +10 -3
  322. package/dist/server.js +11 -4
  323. package/dist/styles.css +29 -1
  324. package/dist/tailwind-utilities.css +1 -1
  325. package/dist/ui/components/Form.d.ts +11 -2
  326. package/dist/ui/components/Form.js +4 -3
  327. package/dist/ui/components/FormField.d.ts +3 -1
  328. package/dist/ui/components/FormField.js +22 -4
  329. package/dist/ui/components/ImageLightbox.js +37 -3
  330. package/dist/ui/components/ImageLightbox.style.css +28 -0
  331. package/dist/ui/components/ListingLayout.d.ts +1 -2
  332. package/dist/ui/components/ListingLayout.js +1 -1
  333. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  334. package/dist/ui/components/RecordDetailModal.js +8 -0
  335. package/dist/ui/components/SlottedListingView.js +9 -1
  336. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  337. package/dist/ui/forms/FormErrorSummary.js +37 -0
  338. package/dist/ui/forms/FormShell.d.ts +37 -2
  339. package/dist/ui/forms/FormShell.js +37 -7
  340. package/dist/ui/forms/FormShell.style.css +17 -0
  341. package/dist/ui/forms/index.d.ts +2 -0
  342. package/dist/ui/forms/index.js +1 -0
  343. package/dist/ui/index.d.ts +4 -0
  344. package/dist/ui/index.js +5 -0
  345. package/dist/utils/media-url.d.ts +12 -1
  346. package/dist/utils/media-url.js +59 -1
  347. package/dist/validation/schemas.d.ts +4 -4
  348. package/package.json +1 -1
  349. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  350. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  351. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  352. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  353. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  354. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  355. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  356. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  357. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  358. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  359. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  360. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  361. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  362. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  363. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  364. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  365. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  366. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  367. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  368. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  369. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  370. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  371. package/dist/features/auth/consent-otp.d.ts +0 -57
  372. package/dist/features/auth/consent-otp.js +0 -127
  373. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  374. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  375. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  376. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  377. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  378. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  379. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  380. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  381. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  382. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  383. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  384. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  385. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  386. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  387. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  388. package/dist/features/live/components/LiveItemsListView.js +0 -44
  389. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  390. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  391. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  392. package/dist/react/hooks/useVisibleItems.js +0 -59
  393. package/dist/ui/components/TabStrip.d.ts +0 -34
  394. package/dist/ui/components/TabStrip.js +0 -56
  395. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -8,6 +8,7 @@ export { UserAccountHubView } from "./UserAccountHubView";
8
8
  export { ProfileView } from "./ProfileView";
9
9
  export { UserSettingsView } from "./UserSettingsView";
10
10
  export { UserOrdersView } from "./UserOrdersView";
11
+ export { UserBidsView } from "./UserBidsView";
11
12
  export { OrderDetailView } from "./OrderDetailView";
12
13
  export { UserAddressesView } from "./UserAddressesView";
13
14
  export { UserNotificationsView } from "./UserNotificationsView";
@@ -17,6 +17,8 @@ import { getProviders } from "../../../contracts";
17
17
  import { getAdminAuth } from "../../../providers/db-firebase";
18
18
  import { isAdminUser } from "../../auth/role-predicates";
19
19
  import { assertPrizeDrawNotLocked, assertPrizeDrawWonItemsImmutable, } from "../../../_internal/server/features/products/service";
20
+ import { recordAdminAction } from "../../../_internal/server/features/audit-log/actions";
21
+ import { AdminAuditActionValues } from "../../audit-log/schemas/firestore";
20
22
  const ERR_UID_REQUIRED = "uid is required";
21
23
  export async function revokeSession(adminId, sessionId) {
22
24
  if (!sessionId?.trim()) {
@@ -85,6 +87,16 @@ export async function adminUpdatePayout(adminId, id, input) {
85
87
  adminId,
86
88
  payoutId: id,
87
89
  });
90
+ if (updateData.status === "paid") {
91
+ void recordAdminAction({
92
+ actorUid: adminId,
93
+ action: AdminAuditActionValues.PAYOUT_MARK_PAID,
94
+ targetType: "payout",
95
+ targetId: id,
96
+ targetLabel: existing.sellerName ?? id,
97
+ metadata: { amount: existing.netAmount ?? existing.amount },
98
+ });
99
+ }
88
100
  return updated;
89
101
  }
90
102
  export async function adminUpdateUser(adminId, uid, input) {
@@ -112,6 +124,16 @@ export async function adminUpdateUser(adminId, uid, input) {
112
124
  targetUid: uid,
113
125
  changes: Object.keys(input),
114
126
  });
127
+ if (input.role !== undefined && input.role !== existing.role) {
128
+ void recordAdminAction({
129
+ actorUid: adminId,
130
+ action: AdminAuditActionValues.USER_ROLE_CHANGE,
131
+ targetType: "user",
132
+ targetId: uid,
133
+ targetLabel: existing.displayName ?? uid,
134
+ metadata: { fromRole: existing.role, toRole: input.role },
135
+ });
136
+ }
115
137
  return updated;
116
138
  }
117
139
  /**
@@ -7,6 +7,8 @@
7
7
  import { couponsRepository } from "../../promotions/repository/coupons.repository";
8
8
  import { NotFoundError, ValidationError } from "../../../errors";
9
9
  import { serverLogger } from "../../../monitoring";
10
+ import { recordAdminAction } from "../../../_internal/server/features/audit-log/actions";
11
+ import { AdminAuditActionValues } from "../../audit-log/schemas/firestore";
10
12
  export async function adminCreateCoupon(adminId, input) {
11
13
  const coupon = await couponsRepository.create({
12
14
  ...input,
@@ -60,6 +62,14 @@ export async function adminUpdateCoupon(adminId, id, input) {
60
62
  adminId,
61
63
  couponId: id,
62
64
  });
65
+ void recordAdminAction({
66
+ actorUid: adminId,
67
+ action: AdminAuditActionValues.COUPON_UPDATE,
68
+ targetType: "coupon",
69
+ targetId: id,
70
+ targetLabel: existing.code,
71
+ metadata: { changedFields: Object.keys(input) },
72
+ });
63
73
  return updated;
64
74
  }
65
75
  export async function adminDeleteCoupon(adminId, id) {
@@ -3,6 +3,7 @@ import { notificationRepository } from "../repository/notification.repository";
3
3
  import { siteSettingsRepository } from "../repository/site-settings.repository";
4
4
  import { userRepository } from "../../auth/repository/user.repository";
5
5
  import { createResendProvider } from "../../../providers/email-resend/provider";
6
+ import { isChannelHealthy, withChannelRetry } from "../../../_internal/server/notifications/channel-health";
6
7
  import { enqueueJob } from "../../jobs/actions/enqueue-job";
7
8
  import { serverLogger } from "../../../monitoring";
8
9
  import { decryptPii } from "../../../security/index";
@@ -131,15 +132,15 @@ export async function sendNotification(input) {
131
132
  const apiKey = creds.resendApiKey?.trim() ?? "";
132
133
  const fromEmail = settings.emailSettings?.fromEmail ?? "noreply@letitrip.in";
133
134
  const fromName = settings.emailSettings?.fromName ?? "LetItRip";
134
- if (apiKey && !apiKey.includes("PLACEHOLDER")) {
135
+ if (apiKey && !apiKey.includes("PLACEHOLDER") && (await isChannelHealthy("email"))) {
135
136
  try {
136
137
  const emailProvider = createResendProvider({ apiKey, fromEmail, fromName });
137
- await emailProvider.send({
138
+ await withChannelRetry("email", () => emailProvider.send({
138
139
  to: resolvedEmail,
139
140
  subject: title,
140
141
  html: emailHtml ?? `<p>${message}</p>`,
141
142
  text: message,
142
- });
143
+ }));
143
144
  emailStatus = "sent";
144
145
  }
145
146
  catch (err) {
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
- import type { StackedViewShellProps } from "../../../ui";
3
2
  export type AdminAdStatus = "draft" | "active" | "scheduled" | "paused";
4
3
  export type AdminAdProvider = "manual" | "adsense" | "thirdParty";
5
4
  export interface AdminAdItem {
@@ -14,7 +13,7 @@ export interface AdminAdItem {
14
13
  endAt?: string;
15
14
  updatedAt?: string;
16
15
  }
17
- export interface AdminAdsViewProps extends Omit<StackedViewShellProps, "sections"> {
16
+ export interface AdminAdsViewProps {
18
17
  endpoint?: string;
19
18
  labels?: {
20
19
  title?: string;
@@ -22,4 +21,4 @@ export interface AdminAdsViewProps extends Omit<StackedViewShellProps, "sections
22
21
  createHref?: string;
23
22
  renderEditLink?: (item: AdminAdItem) => React.ReactNode;
24
23
  }
25
- export declare function AdminAdsView({ endpoint, labels, createHref, renderEditLink, ...rest }: AdminAdsViewProps): React.JSX.Element;
24
+ export declare function AdminAdsView({ endpoint, labels, createHref, renderEditLink, }: AdminAdsViewProps): React.JSX.Element;
@@ -1,65 +1,30 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
- import { useApiMutation } from "@mohasinac/appkit/client";
4
+ import { useApiMutation, sieveFilter, SIEVE_OP, sortBy } from "@mohasinac/appkit/client";
5
5
  import React from "react";
6
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
7
- import { Alert, Button, Checkbox, Div, Input, Label, Row, Select, Stack, StackedViewShell, Text, TextLink } from "../../../ui";
7
+ import { Alert, Button, Checkbox, Div, Input, Label, PaginatedSelect, Row, Select, Stack, Text, TextLink } from "../../../ui";
8
8
  import { apiClient } from "../../../http";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
- import { DataTable } from "./DataTable";
10
+ import { DataListingView } from "./DataListingView";
11
11
  const __P = {
12
12
  p3: "p-[var(--appkit-space-3)]",
13
13
  };
14
14
  function AdsSettingsPanel({ adsenseClientId, setAdsenseClientId, thirdPartyScriptUrl, setThirdPartyScriptUrl, consentRequired, setConsentRequired, serverCredentialIssues, localCredentialIssues, credentialStatus, providerCredentialsMasked, settingsMutation, hasPendingCredentialInput, currentConsentRequired, settingsMessage, onSave, }) {
15
15
  return (_jsxs(Stack, { border: "default", className: __P.p3, gap: "3", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Provider and publish settings" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Save provider credentials here before publishing AdSense or third-party inventory." }), serverCredentialIssues.length > 0 ? (_jsx(Alert, { variant: "warning", title: "Provider credentials need attention", children: serverCredentialIssues.join("; ") })) : null, _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-2", children: [_jsx(Input, { label: "AdSense client id", value: adsenseClientId, onChange: (event) => setAdsenseClientId(event.target.value), placeholder: providerCredentialsMasked?.adsenseClientId || "ca-pub-XXXXXXXXXX" }), _jsx(Input, { label: "Third-party script URL", value: thirdPartyScriptUrl, onChange: (event) => setThirdPartyScriptUrl(event.target.value), placeholder: providerCredentialsMasked?.thirdPartyScriptUrl || "https://..." })] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Stored credentials: AdSense ", credentialStatus?.hasAdsenseClientId ? "configured" : "missing", " \u00B7 Third-party ", credentialStatus?.hasThirdPartyScriptUrl ? "configured" : "missing"] }), _jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsxs(Label, { layout: "flex", gap: "md", size: "sm", children: [_jsx(Checkbox, { bare: true, checked: consentRequired, onChange: (event) => setConsentRequired(event.target.checked) }), "Require consent globally for ad rendering"] }), _jsx(Button, { size: "sm", variant: "outline", disabled: settingsMutation.isPending || localCredentialIssues.length > 0 || (!hasPendingCredentialInput && consentRequired === Boolean(currentConsentRequired)), onClick: onSave, children: settingsMutation.isPending ? "Saving..." : "Save settings" })] }), localCredentialIssues.length > 0 ? (_jsx(Alert, { variant: "error", title: "Fix settings before saving", children: localCredentialIssues.join("; ") })) : null, settingsMessage ? (_jsx(Alert, { variant: settingsMessage.toLowerCase().includes("failed") ? "error" : "success", title: "Settings", children: settingsMessage })) : null] }));
16
16
  }
17
- function AdsFilterRow({ q, setQ, status, setStatus, provider, setProvider, placement, setPlacement, placements, onPageReset }) {
18
- return (_jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-4", children: [_jsx(Input, { label: "Search", value: q, onChange: (event) => { onPageReset(); setQ(event.target.value); }, placeholder: "Search ads" }), _jsx(Select, { label: "Status", value: status, options: [
19
- { label: "All", value: "all" },
20
- { label: "Draft", value: "draft" },
21
- { label: "Active", value: "active" },
22
- { label: "Scheduled", value: "scheduled" },
23
- { label: "Paused", value: "paused" },
24
- ], onChange: (event) => { onPageReset(); setStatus(event.target.value); } }), _jsx(Select, { label: "Provider", value: provider, options: [
25
- { label: "All", value: "all" },
26
- { label: "Manual", value: "manual" },
27
- { label: "AdSense", value: "adsense" },
28
- { label: "Third Party", value: "thirdParty" },
29
- ], onChange: (event) => { onPageReset(); setProvider(event.target.value); } }), _jsx(Select, { label: "Placement", value: placement, options: [
30
- { label: "All", value: "all" },
31
- ...placements.map((item) => ({ label: item.label, value: item.id })),
32
- ], onChange: (event) => { onPageReset(); setPlacement(event.target.value); } })] }));
33
- }
34
- export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, createHref = "/admin/ads/new", renderEditLink, ...rest }) {
17
+ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, createHref = "/admin/ads/new", renderEditLink, }) {
35
18
  const queryClient = useQueryClient();
36
- const [page, setPage] = React.useState(1);
37
- const [pageSize] = React.useState(20);
38
- const [q, setQ] = React.useState("");
39
- const [status, setStatus] = React.useState("all");
40
- const [provider, setProvider] = React.useState("all");
41
- const [placement, setPlacement] = React.useState("all");
42
19
  const [consentRequired, setConsentRequired] = React.useState(false);
43
20
  const [adsenseClientId, setAdsenseClientId] = React.useState("");
44
21
  const [thirdPartyScriptUrl, setThirdPartyScriptUrl] = React.useState("");
45
22
  const [settingsMessage, setSettingsMessage] = React.useState(null);
46
- const queryKey = ["admin-ads", endpoint, page, pageSize, q, status, provider, placement];
47
- const adsQuery = useQuery({
48
- queryKey,
49
- queryFn: () => {
50
- const sp = new URLSearchParams();
51
- sp.set("page", String(page));
52
- sp.set("pageSize", String(pageSize));
53
- if (q.trim())
54
- sp.set("q", q.trim());
55
- if (status !== "all")
56
- sp.set("status", status);
57
- if (provider !== "all")
58
- sp.set("provider", provider);
59
- if (placement !== "all")
60
- sp.set("placement", placement);
61
- return apiClient.get(`${endpoint}?${sp.toString()}`);
62
- },
23
+ // Lightweight metadata-only fetch (placements, consent, credential status)
24
+ // decoupled from the paginated listing query DataListingView owns internally.
25
+ const metaQuery = useQuery({
26
+ queryKey: ["admin-ads-meta", endpoint],
27
+ queryFn: () => apiClient.get(`${endpoint}?page=1&pageSize=1`),
63
28
  staleTime: 10000,
64
29
  });
65
30
  const statusMutation = useApiMutation({
@@ -68,13 +33,14 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
68
33
  },
69
34
  onSuccess: async () => {
70
35
  await queryClient.invalidateQueries({ queryKey: ["admin-ads"] });
36
+ await queryClient.invalidateQueries({ queryKey: ["admin-ads-meta"] });
71
37
  },
72
38
  });
73
39
  React.useEffect(() => {
74
- if (!adsQuery.data)
40
+ if (!metaQuery.data)
75
41
  return;
76
- setConsentRequired(Boolean(adsQuery.data.consentRequired));
77
- }, [adsQuery.data]);
42
+ setConsentRequired(Boolean(metaQuery.data.consentRequired));
43
+ }, [metaQuery.data]);
78
44
  const settingsMutation = useApiMutation({
79
45
  mutationFn: async (payload) => {
80
46
  await apiClient.patch(endpoint, payload);
@@ -83,46 +49,14 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
83
49
  setSettingsMessage("Ad settings saved.");
84
50
  setAdsenseClientId("");
85
51
  setThirdPartyScriptUrl("");
86
- await queryClient.invalidateQueries({ queryKey: ["admin-ads"] });
52
+ await queryClient.invalidateQueries({ queryKey: ["admin-ads-meta"] });
87
53
  },
88
54
  onError: (error) => {
89
55
  setSettingsMessage(error instanceof Error ? error.message : "Failed to save ad settings");
90
56
  },
91
57
  });
92
- const rows = (adsQuery.data?.items ?? []);
93
- const columns = React.useMemo(() => [
94
- {
95
- key: "name",
96
- header: "Ad",
97
- render: (row) => (_jsxs(Div, { children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.id })] })),
98
- },
99
- {
100
- key: "provider",
101
- header: "Provider",
102
- render: (row) => _jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.provider }),
103
- },
104
- {
105
- key: "status",
106
- header: "Status",
107
- render: (row) => (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.status }), row.publishReady === false ? (_jsx(Text, { className: "text-[11px] text-error", children: "Publish blocked" })) : null] })),
108
- },
109
- {
110
- key: "placementIds",
111
- header: "Placements",
112
- render: (row) => (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.placementIds.join(", ") })),
113
- },
114
- {
115
- key: "actions",
116
- header: "Actions",
117
- render: (row) => {
118
- const nextStatus = row.status === "active" ? "paused" : "active";
119
- const cannotPublish = nextStatus === "active" && row.publishReady === false;
120
- return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-neutral-200 border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
121
- },
122
- },
123
- ], [renderEditLink, statusMutation]);
124
- const placements = adsQuery.data?.placements ?? [];
125
- const credentialStatus = adsQuery.data?.providerCredentialStatus;
58
+ const placements = metaQuery.data?.placements ?? [];
59
+ const credentialStatus = metaQuery.data?.providerCredentialStatus;
126
60
  const serverCredentialIssues = credentialStatus?.issues ?? [];
127
61
  const localCredentialIssues = React.useMemo(() => {
128
62
  const issues = [];
@@ -155,11 +89,74 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
155
89
  };
156
90
  settingsMutation.mutate(payload);
157
91
  };
158
- return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: labels.title ?? "Ad Inventory", sections: [
159
- _jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { variant: "secondary", children: "Manage ad inventory, placement mapping, and publishing state." }), _jsx(TextLink, { variant: "bare", href: createHref, rounded: "md", paddingX: "sm", size: "sm", weight: "medium", layout: "inline-flex", align: "center", className: "h-9 bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: "New ad" })] }),
160
- adsQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ads", children: adsQuery.error instanceof Error ? adsQuery.error.message : "Unknown error" })) : null,
161
- _jsx(AdsSettingsPanel, { adsenseClientId: adsenseClientId, setAdsenseClientId: setAdsenseClientId, thirdPartyScriptUrl: thirdPartyScriptUrl, setThirdPartyScriptUrl: setThirdPartyScriptUrl, consentRequired: consentRequired, setConsentRequired: setConsentRequired, serverCredentialIssues: serverCredentialIssues, localCredentialIssues: localCredentialIssues, credentialStatus: credentialStatus, providerCredentialsMasked: adsQuery.data?.providerCredentialsMasked, settingsMutation: settingsMutation, hasPendingCredentialInput: hasPendingCredentialInput, currentConsentRequired: Boolean(adsQuery.data?.consentRequired), settingsMessage: settingsMessage, onSave: saveSettings }),
162
- _jsx(AdsFilterRow, { q: q, setQ: setQ, status: status, setStatus: setStatus, provider: provider, setProvider: setProvider, placement: placement, setPlacement: setPlacement, placements: placements, onPageReset: () => setPage(1) }),
163
- _jsx(DataTable, { columns: columns, rows: rows, isLoading: adsQuery.isLoading, currentPage: adsQuery.data?.page ?? page, totalPages: adsQuery.data?.totalPages ?? 1, onPageChange: setPage, emptyLabel: "No ads found" }),
164
- ] }));
92
+ const columns = [
93
+ {
94
+ key: "name",
95
+ header: "Ad",
96
+ render: (row) => (_jsxs(Div, { children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.id })] })),
97
+ },
98
+ {
99
+ key: "provider",
100
+ header: "Provider",
101
+ render: (row) => _jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.provider }),
102
+ },
103
+ {
104
+ key: "status",
105
+ header: "Status",
106
+ render: (row) => (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.status }), row.publishReady === false ? (_jsx(Text, { className: "text-[11px] text-error", children: "Publish blocked" })) : null] })),
107
+ },
108
+ {
109
+ key: "placementIds",
110
+ header: "Placements",
111
+ render: (row) => (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.placementIds.join(", ") })),
112
+ },
113
+ {
114
+ key: "actions",
115
+ header: "Actions",
116
+ render: (row) => {
117
+ const nextStatus = row.status === "active" ? "paused" : "active";
118
+ const cannotPublish = nextStatus === "active" && row.publishReady === false;
119
+ return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-neutral-200 border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
120
+ },
121
+ },
122
+ ];
123
+ const config = {
124
+ portal: "admin",
125
+ title: labels.title ?? "Ad Inventory",
126
+ searchPlaceholder: "Search ads",
127
+ emptyLabel: "No ads found",
128
+ filterKeys: ["status", "provider", "placement"],
129
+ defaultSort: sortBy("updatedAt", "DESC"),
130
+ queryKey: ["admin-ads", endpoint],
131
+ endpoint,
132
+ sortOptions: [{ value: sortBy("updatedAt", "DESC"), label: "Recently updated" }],
133
+ columns,
134
+ mapRows: (response) => response.items,
135
+ getTotal: (response) => response.total,
136
+ buildFilters: (state) => {
137
+ const clauses = [];
138
+ if (state.status)
139
+ clauses.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
140
+ if (state.provider)
141
+ clauses.push(sieveFilter("provider", SIEVE_OP.EQ, state.provider));
142
+ if (state.placement)
143
+ clauses.push(sieveFilter("placement", SIEVE_OP.EQ, state.placement));
144
+ return clauses.length ? clauses.join(",") : undefined;
145
+ },
146
+ toolbarExtra: (_jsx(TextLink, { variant: "bare", href: createHref, rounded: "md", paddingX: "sm", size: "sm", weight: "medium", layout: "inline-flex", align: "center", className: "h-9 bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: "New ad" })),
147
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Select, { label: "Status", value: pendingFilters.status || "all", options: [
148
+ { label: "All", value: "all" },
149
+ { label: "Draft", value: "draft" },
150
+ { label: "Active", value: "active" },
151
+ { label: "Scheduled", value: "scheduled" },
152
+ { label: "Paused", value: "paused" },
153
+ ], onChange: (e) => setPendingFilters((p) => ({ ...p, status: e.target.value === "all" ? "" : e.target.value })) }), _jsx(Select, { label: "Provider", value: pendingFilters.provider || "all", options: [
154
+ { label: "All", value: "all" },
155
+ { label: "Manual", value: "manual" },
156
+ { label: "AdSense", value: "adsense" },
157
+ { label: "Third Party", value: "thirdParty" },
158
+ ], onChange: (e) => setPendingFilters((p) => ({ ...p, provider: e.target.value === "all" ? "" : e.target.value })) }), _jsx(Label, { size: "sm", color: "muted", children: "Placement" }), _jsx(PaginatedSelect, { value: pendingFilters.placement || undefined, onChange: (v) => setPendingFilters((p) => ({ ...p, placement: v ?? "" })), options: placements.map((item) => ({ value: item.id, label: item.label })) })] })),
159
+ renderAboveContent: () => (_jsxs(Stack, { gap: "md", className: __P.p3, children: [metaQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ads", children: metaQuery.error instanceof Error ? metaQuery.error.message : "Unknown error" })) : null, _jsx(AdsSettingsPanel, { adsenseClientId: adsenseClientId, setAdsenseClientId: setAdsenseClientId, thirdPartyScriptUrl: thirdPartyScriptUrl, setThirdPartyScriptUrl: setThirdPartyScriptUrl, consentRequired: consentRequired, setConsentRequired: setConsentRequired, serverCredentialIssues: serverCredentialIssues, localCredentialIssues: localCredentialIssues, credentialStatus: credentialStatus, providerCredentialsMasked: metaQuery.data?.providerCredentialsMasked, settingsMutation: settingsMutation, hasPendingCredentialInput: hasPendingCredentialInput, currentConsentRequired: Boolean(metaQuery.data?.consentRequired), settingsMessage: settingsMessage, onSave: saveSettings })] })),
160
+ };
161
+ return _jsx(DataListingView, { config: config });
165
162
  }
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useApiMutation } from "@mohasinac/appkit/client";
4
4
  import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
5
5
  import { sortBy } from "@mohasinac/appkit/client";
6
6
  import React from "react";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
- import { FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
8
+ import { FilterChipGroup, ListingLayout, RecordDetailModal, RowActionMenu, useToast } from "../../../ui";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
11
11
  import { ADMIN_EVENT_ENTRY_STATUS_TABS } from "../constants/filter-tabs";
@@ -15,6 +15,7 @@ import { apiClient } from "../../../http";
15
15
  export function AdminAllEventEntriesView({ children, ...props }) {
16
16
  const queryClient = useQueryClient();
17
17
  const { showToast } = useToast();
18
+ const [selected, setSelected] = React.useState(null);
18
19
  const updateMutation = useApiMutation({
19
20
  mutationFn: async ({ id, status }) => {
20
21
  await apiClient.patch(ADMIN_ENDPOINTS.ADMIN_EVENT_ENTRY_BY_ID(id), { status });
@@ -52,10 +53,18 @@ export function AdminAllEventEntriesView({ children, ...props }) {
52
53
  status: toStringValue(item.status ?? item.reviewStatus, "PENDING"),
53
54
  updatedAt: toRelativeDate(item.submittedAt ?? item.createdAt),
54
55
  eventId: toStringValue(item.eventId, ""),
56
+ _raw: item,
55
57
  })),
56
58
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
57
59
  buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
60
+ // Confirm/waitlist/cancel used to be the only affordances — an admin
61
+ // decided an entry's fate without ever seeing the entrant's answers.
62
+ onRowClick: (row) => setSelected(row),
58
63
  renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
64
+ {
65
+ label: "View details",
66
+ onClick: () => setSelected(row),
67
+ },
59
68
  {
60
69
  label: ACTIONS.ADMIN["confirm-entry"].label,
61
70
  onClick: () => updateMutation.mutate({ id: row.id, status: "CONFIRMED" }),
@@ -72,5 +81,11 @@ export function AdminAllEventEntriesView({ children, ...props }) {
72
81
  ] })),
73
82
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_EVENT_ENTRY_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
74
83
  };
75
- return _jsx(DataListingView, { config: config });
84
+ const raw = selected?._raw ?? {};
85
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: Boolean(selected), onClose: () => setSelected(null), title: selected?.primary ?? "Event Entry", badges: [{ label: selected?.status ?? "PENDING", variant: "default" }], fields: [
86
+ { label: "Event", value: toStringValue(raw.eventId, "—") },
87
+ { label: "Email", value: toStringValue(raw.userEmail, "—") },
88
+ { label: "Points", value: String(raw.points ?? "—") },
89
+ { label: "Submitted", value: selected?.updatedAt ?? "—" },
90
+ ], metadata: raw.responses ?? undefined })] }));
76
91
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AdminAuditLogView — read-only list of admin action audit entries.
3
+ * Entries are written exclusively via `recordAdminAction()` from the
4
+ * instrumented privileged-action call sites (bans, checkout bypass, coupon
5
+ * edits, payout mark-paid, store status changes, role changes) — this view
6
+ * has no create/edit/delete affordance, only "view details".
7
+ */
8
+ import React from "react";
9
+ import type { ListingLayoutProps } from "../../../ui";
10
+ export type AdminAuditLogViewProps = ListingLayoutProps;
11
+ export declare function AdminAuditLogView({ children, ...props }: AdminAuditLogViewProps): React.JSX.Element;
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ /**
4
+ * AdminAuditLogView — read-only list of admin action audit entries.
5
+ * Entries are written exclusively via `recordAdminAction()` from the
6
+ * instrumented privileged-action call sites (bans, checkout bypass, coupon
7
+ * edits, payout mark-paid, store status changes, role changes) — this view
8
+ * has no create/edit/delete affordance, only "view details".
9
+ */
10
+ import React, { useState } from "react";
11
+ import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
12
+ import { sortBy } from "@mohasinac/appkit/client";
13
+ import { FilterChipGroup, Input, ListingLayout, RowActionMenu, Stack } from "../../../ui";
14
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
15
+ import { ADMIN_AUDIT_LOG_ACTION_TABS } from "../constants/filter-tabs";
16
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
17
+ import { DataListingView } from "./DataListingView";
18
+ import { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
19
+ const ACTION_LABELS = {
20
+ user_hard_ban: "User hard-banned",
21
+ user_soft_ban: "User soft-banned",
22
+ user_unban: "User unbanned",
23
+ checkout_bypass: "Admin checkout bypass",
24
+ coupon_update: "Coupon updated",
25
+ payout_mark_paid: "Payout marked paid",
26
+ store_status_change: "Store status changed",
27
+ user_role_change: "User role changed",
28
+ };
29
+ export function AdminAuditLogView({ children, ...props }) {
30
+ const [viewEntry, setViewEntry] = useState(null);
31
+ if (React.Children.count(children) > 0) {
32
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
33
+ }
34
+ const openEntry = (row) => {
35
+ const raw = row._raw;
36
+ setViewEntry({
37
+ id: row.id,
38
+ actorUid: toStringValue(raw.actorUid, ""),
39
+ actorName: toStringValue(raw.actorName, "") || undefined,
40
+ action: toStringValue(raw.action, ""),
41
+ targetType: toStringValue(raw.targetType, ""),
42
+ targetId: toStringValue(raw.targetId, ""),
43
+ targetLabel: toStringValue(raw.targetLabel, "") || undefined,
44
+ reason: toStringValue(raw.reason, "") || undefined,
45
+ metadata: raw.metadata ?? undefined,
46
+ createdAt: toStringValue(raw.createdAt, "") || undefined,
47
+ });
48
+ };
49
+ const config = {
50
+ portal: "admin",
51
+ title: "Audit Log",
52
+ searchPlaceholder: "Search by actor uid",
53
+ emptyLabel: "No audit log entries found",
54
+ filterKeys: ["action", "actorUid"],
55
+ defaultSort: sortBy("createdAt", "DESC"),
56
+ queryKey: ["admin", "audit-log", "listing"],
57
+ endpoint: ADMIN_ENDPOINTS.AUDIT_LOG,
58
+ sortOptions: [
59
+ { value: sortBy("createdAt", "DESC"), label: "Newest" },
60
+ { value: sortBy("createdAt", "ASC"), label: "Oldest" },
61
+ ],
62
+ mapRows: (response) => toRecordArray(response.entries).map((item, index) => {
63
+ const action = toStringValue(item.action, "unknown");
64
+ return {
65
+ id: toStringValue(item.id, `audit-${index}`),
66
+ primary: ACTION_LABELS[action] ?? action,
67
+ secondary: [
68
+ toStringValue(item.actorName ?? item.actorUid, "Unknown actor"),
69
+ `${toStringValue(item.targetType, "")}: ${toStringValue(item.targetLabel ?? item.targetId, "")}`,
70
+ ].join(" · "),
71
+ status: action,
72
+ updatedAt: toRelativeDate(item.createdAt),
73
+ _raw: item,
74
+ };
75
+ }),
76
+ getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
77
+ onRowClick: (row) => openEntry(row),
78
+ buildFilters: (state) => {
79
+ const parts = [];
80
+ if (state.action && state.action !== "All")
81
+ parts.push(sieveFilter("action", SIEVE_OP.EQ, state.action));
82
+ if (state.actorUid)
83
+ parts.push(sieveFilter("actorUid", SIEVE_OP.EQ, state.actorUid));
84
+ return parts.length > 0 ? parts.join(",") : undefined;
85
+ },
86
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
87
+ {
88
+ label: "View details",
89
+ onClick: () => openEntry(row),
90
+ },
91
+ ] })),
92
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Action", tabs: ADMIN_AUDIT_LOG_ACTION_TABS, value: pendingFilters.action ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, action: id })) }), _jsx(Input, { label: "Actor UID", value: pendingFilters.actorUid ?? "", onChange: (e) => setPendingFilters((p) => ({ ...p, actorUid: e.target.value })), placeholder: "Filter by actor uid" })] })),
93
+ };
94
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ViewAuditLogEntryModal, { entry: viewEntry, isOpen: Boolean(viewEntry), onClose: () => setViewEntry(null) })] }));
95
+ }
@@ -1,14 +1,26 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { useApiMutation } from "@mohasinac/appkit/client";
5
- import React from "react";
5
+ import React, { useEffect, useMemo } from "react";
6
+ import { z } from "zod";
6
7
  import { useQuery } from "@tanstack/react-query";
7
8
  import { Button, ConfirmDeleteModal, Div, Heading, Input, RichTextEditor, RichTextRenderer, Row, Select, Span, Stack, StackedViewShell, TagInput, Text, Toggle, useToast } from "../../../ui";
9
+ import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms";
8
10
  import { ImageUpload, MediaImage, useMediaUpload } from "../../media";
9
11
  import { apiClient } from "../../../http";
10
12
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
13
+ import { blogPostCategorySchema } from "../../blog/schemas";
11
14
  import { StepForm } from "../../shell";
15
+ // Form-input subset — `blogPostSchema` (blog schemas) requires `id` and uses
16
+ // a `MediaField` shape for `coverImage`, neither of which this draft has
17
+ // (coverImage here is a plain uploaded-URL string; id doesn't exist pre-save).
18
+ const blogDraftSchema = z.object({
19
+ title: z.string().min(1, "Title is required"),
20
+ slug: z.string().min(1, "Slug is required").regex(/^blog-/, "Slug must start with 'blog-'"),
21
+ category: blogPostCategorySchema,
22
+ metaDescription: z.string().max(160).optional().or(z.literal("")),
23
+ }).passthrough();
12
24
  const __P = {
13
25
  p4: "p-[var(--appkit-space-4)]",
14
26
  };
@@ -72,6 +84,11 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
72
84
  const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
73
85
  const { showToast } = useToast();
74
86
  const { upload } = useMediaUpload();
87
+ const { shellCtx, setFieldError, validate } = useFormShellState(blogDraftSchema);
88
+ useEffect(() => {
89
+ validate(draft);
90
+ // eslint-disable-next-line react-hooks/exhaustive-deps
91
+ }, [draft, validate]);
75
92
  const update = React.useCallback((partial) => {
76
93
  setDraft((prev) => ({ ...prev, ...partial }));
77
94
  }, []);
@@ -146,6 +163,10 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
146
163
  onSaved(id);
147
164
  },
148
165
  onError: (err) => {
166
+ const issues = err?.issues;
167
+ if (issues && issues.length > 0) {
168
+ applyZodIssues(issues, setFieldError);
169
+ }
149
170
  showToast(err?.message ?? "Failed to save post.", "error");
150
171
  },
151
172
  });
@@ -163,6 +184,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
163
184
  const steps = [
164
185
  {
165
186
  label: "Content",
187
+ fields: ["title", "slug", "excerpt", "content"],
166
188
  validate: (values) => !values.title.trim() ? "Title is required" : null,
167
189
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Content" }), _jsx(Input, { label: "Title", value: values.title, onChange: (e) => {
168
190
  const v = e.target.value;
@@ -174,6 +196,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
174
196
  },
175
197
  {
176
198
  label: "Media",
199
+ fields: ["coverImage", "youtubeId"],
177
200
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Media" }), _jsx(ImageUpload, { label: "Cover Image", currentImage: values.coverImage, onUpload: (file) => upload(file, "blog", true, {
178
201
  type: "blog-cover",
179
202
  title: values.title || values.slug,
@@ -182,6 +205,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
182
205
  },
183
206
  {
184
207
  label: "SEO & Tags",
208
+ fields: ["category", "tags", "metaTitle", "metaDescription"],
185
209
  render: ({ values, onChange }) => {
186
210
  const readTime = Math.max(1, Math.round(values.content.replace(/<[^>]*>/g, "").split(/\s+/).filter(Boolean).length / 200));
187
211
  return (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "SEO & Tags" }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: values.category, onValueChange: (v) => onChange({ category: v }) }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-1.5", size: "sm", weight: "medium", children: "Estimated Read Time" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["~", readTime, " min (auto-calculated from content)"] })] })] }), _jsx(TagInput, { label: "Tags", value: values.tags, onChange: (t) => onChange({ tags: t }), placeholder: "e.g. pokemon, grading, tcg" }), _jsx(Input, { label: "Meta Title (optional)", value: values.metaTitle, onChange: (e) => onChange({ metaTitle: e.target.value }), placeholder: "Defaults to post title" }), _jsx(Input, { label: "Meta Description (optional)", value: values.metaDescription, onChange: (e) => onChange({ metaDescription: e.target.value }), placeholder: "SEO description \u2014 max 160 chars", maxLength: 160 })] }));
@@ -189,6 +213,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
189
213
  },
190
214
  {
191
215
  label: "Publish",
216
+ fields: ["status", "publishedAt", "authorName", "isFeatured"],
192
217
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Publish Settings" }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: values.status, onValueChange: (v) => onChange({ status: v }) }), _jsx(Input, { label: "Publish date (optional)", value: values.publishedAt, onChange: (e) => onChange({ publishedAt: e.target.value }), type: "date", helperText: "Auto-set to now when publishing." })] }), _jsx(Input, { label: "Author Name", value: values.authorName, onChange: (e) => onChange({ authorName: e.target.value }), placeholder: "Author display name" }), _jsx(Toggle, { label: "Featured post", checked: values.isFeatured, onChange: (checked) => onChange({ isFeatured: checked }) }), isEdit && (_jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: _jsx(Button, { type: "button", variant: "danger", size: "sm", onClick: () => setShowDeleteConfirm(true), isLoading: deleteMutation.isPending, children: "Delete post" }) }))] })),
193
218
  },
194
219
  ];
@@ -196,7 +221,16 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
196
221
  setShowDeleteConfirm(false);
197
222
  deleteMutation.mutate();
198
223
  }, onClose: () => setShowDeleteConfirm(false), title: "Delete post", message: "This will permanently delete the blog post. This cannot be undone." }));
199
- const formContent = (_jsxs(_Fragment, { children: [_jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: () => { saveMutation.mutate(); }, formId: "admin-blog", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: isEdit ? "Save Changes" : "Create Post", isLoading: isLoading }), deleteModal] }));
224
+ const fieldToStepIndex = useMemo(() => {
225
+ const map = {};
226
+ steps.forEach((step, i) => {
227
+ step.fields?.forEach((field) => { map[field] = i; });
228
+ });
229
+ return map;
230
+ // eslint-disable-next-line react-hooks/exhaustive-deps
231
+ }, [steps.length]);
232
+ const wizardShellCtx = useMemo(() => ({ ...shellCtx, fieldToStepIndex, goToStep: (n) => setCurrentStep(n) }), [shellCtx, fieldToStepIndex, setCurrentStep]);
233
+ const formContent = (_jsxs(FormShellContext.Provider, { value: wizardShellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: () => { saveMutation.mutate(); }, formId: "admin-blog", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: isEdit ? "Save Changes" : "Create Post", isLoading: isLoading }), deleteModal] }));
200
234
  if (embedded) {
201
235
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
202
236
  }