@mohasinac/appkit 4.10.0 → 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 (319) 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/audit-log/actions.d.ts +25 -0
  8. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  9. package/dist/_internal/server/features/brands/actions.js +0 -2
  10. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  11. package/dist/_internal/server/features/brands/og.js +1 -1
  12. package/dist/_internal/server/features/grouped/data.d.ts +4 -0
  13. package/dist/_internal/server/features/grouped/data.js +24 -0
  14. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  15. package/dist/_internal/server/features/grouped/index.js +1 -1
  16. package/dist/_internal/server/features/orders/actions.js +9 -0
  17. package/dist/_internal/server/features/orders/adapters.js +25 -0
  18. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  19. package/dist/_internal/server/functions/scheduled.js +17 -1
  20. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  21. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  22. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  23. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  24. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  25. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  26. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  27. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  28. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  29. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  30. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  31. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  32. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  34. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  35. package/dist/_internal/server/notifications/channel-health.js +101 -0
  36. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  37. package/dist/_internal/shared/features/brands/schema.js +0 -1
  38. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  39. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  40. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  41. package/dist/client.d.ts +21 -6
  42. package/dist/client.js +13 -3
  43. package/dist/constants/api-endpoints.d.ts +6 -9
  44. package/dist/constants/api-endpoints.js +2 -3
  45. package/dist/contracts/client-auth.d.ts +3 -7
  46. package/dist/contracts/email.d.ts +1 -0
  47. package/dist/features/account/components/AddressForm.js +24 -2
  48. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  49. package/dist/features/account/components/BecomeSellerView.js +1 -1
  50. package/dist/features/account/components/NotificationBell.js +1 -1
  51. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  52. package/dist/features/account/components/UserBidsView.js +47 -0
  53. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  54. package/dist/features/account/components/UserOrdersView.js +54 -4
  55. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  56. package/dist/features/account/components/UserReturnsView.js +37 -3
  57. package/dist/features/account/components/index.d.ts +3 -2
  58. package/dist/features/account/components/index.js +1 -0
  59. package/dist/features/admin/actions/admin-actions.js +22 -0
  60. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  61. package/dist/features/admin/actions/notification-actions.js +4 -3
  62. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  63. package/dist/features/admin/components/AdminAdsView.js +86 -89
  64. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  65. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  66. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  67. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  68. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  69. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  70. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  71. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  72. package/dist/features/admin/components/AdminCartsView.js +34 -4
  73. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  74. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  75. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  76. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  77. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  78. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  79. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  80. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  81. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  82. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  83. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  84. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  85. package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
  86. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  87. package/dist/features/admin/components/AdminStoresView.js +7 -0
  88. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  89. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  90. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  91. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  92. package/dist/features/admin/components/AdminUsersView.js +1 -1
  93. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  94. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  95. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  96. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  97. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  98. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  99. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  100. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  101. package/dist/features/admin/components/index.d.ts +8 -1
  102. package/dist/features/admin/components/index.js +4 -0
  103. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  104. package/dist/features/admin/constants/filter-tabs.js +27 -0
  105. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  106. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  107. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  108. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  109. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  110. package/dist/features/admin/schemas/firestore.d.ts +12 -0
  111. package/dist/features/analytics/types.d.ts +1 -1
  112. package/dist/features/analytics/types.js +8 -0
  113. package/dist/features/auctions/schemas/index.d.ts +5 -0
  114. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  115. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  116. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  117. package/dist/features/audit-log/schemas/firestore.js +35 -0
  118. package/dist/features/auth/actions/index.d.ts +0 -1
  119. package/dist/features/auth/actions/index.js +0 -1
  120. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  121. package/dist/features/auth/hooks/useAuth.js +21 -37
  122. package/dist/features/auth/permissions/constants.d.ts +1 -1
  123. package/dist/features/auth/permissions/constants.js +2 -0
  124. package/dist/features/blog/schemas/index.d.ts +32 -0
  125. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  126. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  127. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  128. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  129. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  130. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  131. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  132. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  133. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  134. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  135. package/dist/features/categories/schemas/index.d.ts +24 -3
  136. package/dist/features/categories/schemas/index.js +3 -1
  137. package/dist/features/categories/types/index.d.ts +7 -0
  138. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  139. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  140. package/dist/features/contact/components/ContactForm.js +17 -4
  141. package/dist/features/contact/email.d.ts +1 -0
  142. package/dist/features/contact/email.js +3 -0
  143. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  144. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  145. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  146. package/dist/features/events/components/EventFormDrawer.js +5 -1
  147. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  148. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  149. package/dist/features/events/schemas/index.d.ts +22 -0
  150. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +4 -0
  151. package/dist/features/grouped/repository/grouped-listings.repository.js +18 -0
  152. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  153. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  154. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  155. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  156. package/dist/features/layout/TitleBarLayout.js +3 -3
  157. package/dist/features/media/MediaImage.js +39 -3
  158. package/dist/features/media/types/index.d.ts +10 -0
  159. package/dist/features/media/types/index.js +1 -0
  160. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  161. package/dist/features/media/upload/MediaUploadField.js +11 -7
  162. package/dist/features/media/upload/video-poster.d.ts +2 -0
  163. package/dist/features/media/upload/video-poster.js +16 -1
  164. package/dist/features/media/upload/video-poster.test.js +1 -1
  165. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  166. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  167. package/dist/features/orders/components/index.d.ts +2 -0
  168. package/dist/features/orders/components/index.js +1 -0
  169. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  170. package/dist/features/orders/constants/payment-window.js +25 -0
  171. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  172. package/dist/features/orders/repository/orders.repository.js +56 -0
  173. package/dist/features/orders/schemas/index.d.ts +24 -24
  174. package/dist/features/orders/types/index.d.ts +33 -0
  175. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  176. package/dist/features/products/components/BidHistory.js +9 -1
  177. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  178. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  179. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  180. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  181. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  182. package/dist/features/products/components/ProductDetailActions.js +1 -1
  183. package/dist/features/products/components/ProductGrid.js +15 -1
  184. package/dist/features/products/constants/action-defs.d.ts +1 -0
  185. package/dist/features/products/constants/action-defs.js +1 -0
  186. package/dist/features/products/schemas/index.d.ts +5 -0
  187. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  188. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  189. package/dist/features/search/components/SearchView.d.ts +1 -3
  190. package/dist/features/search/components/SearchView.js +1 -1
  191. package/dist/features/seller/components/QuickProductForm.js +2 -1
  192. package/dist/features/seller/components/SellerBidsView.js +66 -107
  193. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  194. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  195. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  196. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  197. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  198. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  199. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  200. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  201. package/dist/features/seller/components/SellerProductShell.js +89 -36
  202. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  203. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  204. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  205. package/dist/features/seller/components/SellerProductsView.js +131 -189
  206. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  207. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  208. package/dist/features/seller/components/SellerShippingView.js +41 -4
  209. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  210. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  211. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  212. package/dist/features/seller/components/index.d.ts +2 -2
  213. package/dist/features/seller/components/index.js +2 -2
  214. package/dist/features/shell/FormShell.d.ts +24 -4
  215. package/dist/features/shell/FormShell.js +12 -6
  216. package/dist/features/shell/QuickFormDrawer.js +67 -12
  217. package/dist/features/shell/StepForm.d.ts +30 -1
  218. package/dist/features/shell/StepForm.js +47 -4
  219. package/dist/features/shell/index.d.ts +1 -1
  220. package/dist/features/shell/index.js +1 -1
  221. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  222. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  223. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  224. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  225. package/dist/features/tester/components/TesterHubView.js +3 -2
  226. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  227. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  228. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +404 -15
  229. package/dist/index.d.ts +15 -3
  230. package/dist/index.js +22 -5
  231. package/dist/next/routing/route-map.d.ts +11 -0
  232. package/dist/next/routing/route-map.js +5 -0
  233. package/dist/providers/email-resend/provider.js +1 -0
  234. package/dist/providers/firebase-client/auth.d.ts +1 -0
  235. package/dist/providers/firebase-client/auth.js +7 -1
  236. package/dist/repositories/index.d.ts +1 -0
  237. package/dist/repositories/index.js +1 -0
  238. package/dist/schemas/registry.d.ts +61 -25
  239. package/dist/security/pii-mask.js +1 -1
  240. package/dist/seed/categories-seed-data.js +69 -0
  241. package/dist/seed/products-auctions-seed-data.js +11 -0
  242. package/dist/seed/products-standard-seed-data.js +93 -4
  243. package/dist/seed/site-settings-seed-data.js +13 -1
  244. package/dist/seed/store-extensions-seed-data.js +1 -1
  245. package/dist/server-entry.d.ts +2 -1
  246. package/dist/server-entry.js +20 -2
  247. package/dist/server.d.ts +7 -2
  248. package/dist/server.js +8 -3
  249. package/dist/styles.css +1 -1
  250. package/dist/tailwind-utilities.css +1 -1
  251. package/dist/ui/components/Form.d.ts +11 -2
  252. package/dist/ui/components/Form.js +4 -3
  253. package/dist/ui/components/FormField.d.ts +3 -1
  254. package/dist/ui/components/FormField.js +22 -4
  255. package/dist/ui/components/ListingLayout.d.ts +1 -2
  256. package/dist/ui/components/ListingLayout.js +1 -1
  257. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  258. package/dist/ui/components/RecordDetailModal.js +8 -0
  259. package/dist/ui/components/SlottedListingView.js +9 -1
  260. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  261. package/dist/ui/forms/FormErrorSummary.js +37 -0
  262. package/dist/ui/forms/FormShell.d.ts +37 -2
  263. package/dist/ui/forms/FormShell.js +37 -7
  264. package/dist/ui/forms/FormShell.style.css +17 -0
  265. package/dist/ui/forms/index.d.ts +2 -0
  266. package/dist/ui/forms/index.js +1 -0
  267. package/dist/ui/index.d.ts +4 -0
  268. package/dist/ui/index.js +5 -0
  269. package/dist/utils/media-url.d.ts +3 -1
  270. package/dist/utils/media-url.js +20 -1
  271. package/dist/validation/schemas.d.ts +4 -4
  272. package/package.json +1 -1
  273. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  274. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  275. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  276. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  277. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  278. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  279. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  280. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  281. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  282. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  283. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  284. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  285. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  286. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  287. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  288. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  289. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  290. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  291. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  292. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  293. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  294. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  295. package/dist/features/auth/consent-otp.d.ts +0 -57
  296. package/dist/features/auth/consent-otp.js +0 -127
  297. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  298. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  299. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  300. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  301. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  302. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  303. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  304. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  305. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  306. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  307. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  308. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  309. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  310. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  311. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  312. package/dist/features/live/components/LiveItemsListView.js +0 -44
  313. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  314. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  315. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  316. package/dist/react/hooks/useVisibleItems.js +0 -59
  317. package/dist/ui/components/TabStrip.d.ts +0 -34
  318. package/dist/ui/components/TabStrip.js +0 -56
  319. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -1,21 +1,16 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
- import { Badge, Row, Span, Stack, sortBy } from "@mohasinac/appkit/client";
4
+ import { Badge, sortBy } from "@mohasinac/appkit/client";
5
5
  import { useState, useCallback } from "react";
6
6
  import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
7
- import { Plus } from "lucide-react";
8
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
9
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
10
- import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, useToast, StickyToolbar, } from "../../../ui";
11
- import { useBottomActions } from "../../layout";
7
+ import { ConfirmDeleteModal, Div, RowActionMenu, Text, useToast } from "../../../ui";
12
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
13
9
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
14
10
  import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
15
11
  import { ROUTES } from "../../../next";
16
- import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
17
- import { TABLE_KEYS } from "../../../constants/table-keys";
18
- const PAGE_SIZE = 25;
12
+ import { toRecordArray, toStringValue } from "../hooks/useSellerListingData";
13
+ import { DataListingView } from "../../admin/components/DataListingView";
19
14
  const DEFAULT_SORT = "displayOrder";
20
15
  const SORT_OPTIONS = [
21
16
  { value: sortBy("displayOrder", "ASC"), label: "Display Order" },
@@ -42,40 +37,11 @@ const COLUMNS = [
42
37
  ];
43
38
  export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete, onBulkDelete, onBulkActivate, onBulkDeactivate, }) {
44
39
  const { showToast } = useToast();
45
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
46
- const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
47
40
  const [deleteTargetId, setDeleteTargetId] = useState(null);
48
41
  const { deletingId, handleDelete: performDelete } = useEntityDelete({
49
42
  deleteFn: onDelete,
50
43
  successMessage: "Category deleted.",
51
- onSuccess: () => { refetch?.(); },
52
44
  });
53
- const commitSearch = useCallback(() => { table.set(TABLE_KEYS.QUERY, searchInput.trim()); }, [searchInput, table]);
54
- const resetAll = useCallback(() => {
55
- table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
56
- setSearchInput("");
57
- }, [table]);
58
- const hasActiveState = !!table.get(TABLE_KEYS.QUERY) || table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT;
59
- const { rows, total, isLoading, errorMessage, refetch } = useSellerListingData({
60
- queryKey: ["seller", "store-categories"],
61
- endpoint: SELLER_ENDPOINTS.STORE_CATEGORIES,
62
- page: table.getNumber(TABLE_KEYS.PAGE, 1),
63
- pageSize: PAGE_SIZE,
64
- sorts: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
65
- q: table.get(TABLE_KEYS.QUERY) || undefined,
66
- mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
67
- id: toStringValue(item.id, `cat-${index}`),
68
- raw: item,
69
- label: String(item.label ?? ""),
70
- slug: String(item.slug ?? ""),
71
- productCount: Number(item.productIds?.length ?? 0),
72
- isActive: Boolean(item.isActive),
73
- })),
74
- getTotal: (response, rows) => typeof response.total === "number" ? response.total : rows.length,
75
- });
76
- const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
77
- const totalPages = Math.ceil(total / PAGE_SIZE);
78
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
79
45
  const handleDelete = useCallback(async (id) => {
80
46
  if (!onDelete)
81
47
  return;
@@ -88,11 +54,6 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
88
54
  }
89
55
  setDeleteTargetId(null);
90
56
  }, [onDelete, performDelete, showToast]);
91
- const bulkActions = [
92
- ...(onBulkDelete ? [buildBulkAction(ACTIONS.STORE["delete-listing"], async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
93
- ...(onBulkActivate ? [buildBulkAction(ACTIONS.ADMIN["activate-bundle"], async () => { await onBulkActivate(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
94
- ...(onBulkDeactivate ? [buildBulkAction(ACTIONS.ADMIN["deactivate-bundle"], async () => { await onBulkDeactivate(selection.selectedIds); selection.clearSelection(); refetch?.(); })] : []),
95
- ];
96
57
  const handleNavigateNew = () => {
97
58
  if (onCreateClick) {
98
59
  onCreateClick();
@@ -100,19 +61,50 @@ export function SellerStoreCategoriesView({ onCreateClick, onEditClick, onDelete
100
61
  }
101
62
  window.location.href = String(ROUTES.STORE.STORE_CATEGORIES_NEW);
102
63
  };
103
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
104
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search categories by label...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: "table", onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { gap: "sm", size: "sm", onClick: handleNavigateNew, children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx(Span, { children: "New Category" })] }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), selection.selectedCount > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), isLoading ? (_jsx(Stack, { gap: "sm", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse", border: "subtle", surface: "muted", rounded: "xl" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "text-center", padding: "y-4xl", children: _jsx(Text, { color: "faint", children: "No categories yet \u2014 add your first storefront category" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids), actions: (row) => (_jsx(RowActionMenu, { actions: [
105
- {
106
- label: ACTIONS.STORE["edit-listing"].label,
107
- onClick: () => onEditClick
108
- ? onEditClick(row.id)
109
- : (window.location.href = String(ROUTES.STORE.STORE_CATEGORIES_EDIT(row.id))),
110
- },
111
- ...(onDelete ? [{
112
- label: ACTIONS.STORE["delete-listing"].label,
113
- destructive: true,
114
- onClick: () => setDeleteTargetId(row.id),
115
- disabled: deletingId === row.id,
116
- }] : []),
117
- ] })) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Category", message: "Are you sure you want to delete this category? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
64
+ const config = {
65
+ portal: "seller",
66
+ title: "Store Categories",
67
+ searchPlaceholder: "Search categories by label...",
68
+ emptyLabel: "No categories yet — add your first storefront category",
69
+ filterKeys: [],
70
+ defaultSort: DEFAULT_SORT,
71
+ queryKey: ["seller", "store-categories", "listing"],
72
+ endpoint: SELLER_ENDPOINTS.STORE_CATEGORIES,
73
+ sortOptions: SORT_OPTIONS,
74
+ columns: COLUMNS,
75
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
76
+ id: toStringValue(item.id, `cat-${index}`),
77
+ raw: item,
78
+ label: String(item.label ?? ""),
79
+ slug: String(item.slug ?? ""),
80
+ productCount: Number(item.productIds?.length ?? 0),
81
+ isActive: Boolean(item.isActive),
82
+ })),
83
+ getTotal: (response, rows) => (typeof response.total === "number" ? response.total : rows.length),
84
+ buildFilters: () => undefined,
85
+ primaryAction: { label: "New Category", onClick: handleNavigateNew },
86
+ buildBulkActions: (selection) => {
87
+ const actions = [
88
+ ...(onBulkDelete ? [buildBulkAction(ACTIONS.STORE["delete-listing"], async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); })] : []),
89
+ ...(onBulkActivate ? [buildBulkAction(ACTIONS.ADMIN["activate-bundle"], async () => { await onBulkActivate(selection.selectedIds); selection.clearSelection(); })] : []),
90
+ ...(onBulkDeactivate ? [buildBulkAction(ACTIONS.ADMIN["deactivate-bundle"], async () => { await onBulkDeactivate(selection.selectedIds); selection.clearSelection(); })] : []),
91
+ ];
92
+ return actions;
93
+ },
94
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
95
+ {
96
+ label: ACTIONS.STORE["edit-listing"].label,
97
+ onClick: () => onEditClick
98
+ ? onEditClick(row.id)
99
+ : (window.location.href = String(ROUTES.STORE.STORE_CATEGORIES_EDIT(row.id))),
100
+ },
101
+ ...(onDelete ? [{
102
+ label: ACTIONS.STORE["delete-listing"].label,
103
+ destructive: true,
104
+ onClick: () => setDeleteTargetId(row.id),
105
+ disabled: deletingId === row.id,
106
+ }] : []),
107
+ ] })),
108
+ };
109
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Category", message: "Are you sure you want to delete this category? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
118
110
  }
@@ -1,3 +1,8 @@
1
+ export interface StorefrontSaveResult {
2
+ ok: boolean;
3
+ error?: string;
4
+ issues?: unknown[];
5
+ }
1
6
  export interface StorefrontDraft {
2
7
  storeName?: string;
3
8
  storeDescription?: string;
@@ -21,7 +26,7 @@ export interface StorefrontDraft {
21
26
  }
22
27
  export interface SellerStorefrontViewProps {
23
28
  initialValues?: StorefrontDraft;
24
- onSave: (data: StorefrontDraft) => void | Promise<void>;
29
+ onSave: (data: StorefrontDraft) => Promise<StorefrontSaveResult>;
25
30
  isLoading?: boolean;
26
31
  storeSlug?: string;
27
32
  }
@@ -1,10 +1,24 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useState } from "react";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { z } from "zod";
4
5
  import { StackedViewShell } from "../../../ui";
5
6
  import { Alert, Div, FormField, FormGroup, Heading, Stack, Text, Toggle, } from "../../../ui";
7
+ import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms";
6
8
  import { ImageUpload, useMediaUpload } from "../../media";
7
9
  import { StepForm, useFormShell } from "../../shell";
10
+ // Small local schema for the storefront form-input shape (not the broader
11
+ // `sellerStoreSchema`, which requires id/storeSlug/ownerId/status/createdAt —
12
+ // fields this editable draft never has, so applying it directly would show
13
+ // false "required" errors on every keystroke).
14
+ const storefrontDraftSchema = z.object({
15
+ storeName: z.string().min(2, "Store name is required").max(80),
16
+ storeDescription: z.string().max(10000).optional().or(z.literal("")),
17
+ storeCategory: z.string().max(80).optional().or(z.literal("")),
18
+ bio: z.string().max(300, "Bio must be 300 characters or fewer").optional().or(z.literal("")),
19
+ website: z.string().url("Enter a valid URL").optional().or(z.literal("")),
20
+ vacationMessage: z.string().max(300).optional().or(z.literal("")),
21
+ }).passthrough();
8
22
  export function SellerStorefrontView({ initialValues, onSave, isLoading = false, storeSlug = "store", }) {
9
23
  const [draft, setDraft] = useState(initialValues ?? {});
10
24
  const [saving, setSaving] = useState(false);
@@ -12,6 +26,11 @@ export function SellerStorefrontView({ initialValues, onSave, isLoading = false,
12
26
  const [currentStep, setCurrentStep] = useState(0);
13
27
  const { markDirty, markClean } = useFormShell();
14
28
  const { upload } = useMediaUpload();
29
+ const { shellCtx, setFieldError, clearErrors, validate } = useFormShellState(storefrontDraftSchema);
30
+ useEffect(() => {
31
+ validate(draft);
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ }, [draft, validate]);
15
34
  const update = useCallback((partial) => {
16
35
  setDraft((prev) => ({ ...prev, ...partial }));
17
36
  markDirty();
@@ -21,9 +40,14 @@ export function SellerStorefrontView({ initialValues, onSave, isLoading = false,
21
40
  setSaving(true);
22
41
  setSaved(false);
23
42
  try {
24
- await onSave(draft);
25
- markClean();
26
- setSaved(true);
43
+ const result = await onSave(draft);
44
+ if (result.ok) {
45
+ markClean();
46
+ clearErrors();
47
+ setSaved(true);
48
+ return;
49
+ }
50
+ applyZodIssues(result.issues ?? [], setFieldError);
27
51
  }
28
52
  finally {
29
53
  setSaving(false);
@@ -33,23 +57,36 @@ export function SellerStorefrontView({ initialValues, onSave, isLoading = false,
33
57
  const steps = [
34
58
  {
35
59
  label: "Store Identity",
60
+ fields: ["storeName", "storeDescription", "storeCategory", "bio"],
36
61
  validate: (values) => !values.storeName?.trim() ? "Store name is required" : null,
37
62
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Store Identity" }), _jsx(FormField, { name: "storeName", label: "Store Name", type: "text", value: values.storeName ?? "", onChange: (v) => onChange({ storeName: v }), placeholder: "e.g. Pok\u00E9mon Palace", disabled: busy }), _jsx(FormField, { name: "storeCategory", label: "Store Category", type: "text", value: values.storeCategory ?? "", onChange: (v) => onChange({ storeCategory: v }), placeholder: "e.g. Trading Cards, Action Figures", disabled: busy }), _jsx(FormField, { name: "bio", label: "Short Bio (shown in search results)", type: "textarea", value: values.bio ?? "", onChange: (v) => onChange({ bio: v }), placeholder: "One-liner about your store (max 300 chars)", disabled: busy }), _jsx(FormField, { name: "storeDescription", label: "Full Description", type: "textarea", value: values.storeDescription ?? "", onChange: (v) => onChange({ storeDescription: v }), placeholder: "Detailed store description shown on your public store page\u2026", disabled: busy })] })),
38
63
  },
39
64
  {
40
65
  label: "Branding",
66
+ fields: ["storeLogoURL", "storeBannerURL"],
41
67
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Branding" }), _jsxs(FormGroup, { columns: 2, children: [_jsx(ImageUpload, { label: "Store Logo", currentImage: values.storeLogoURL, onUpload: (file) => upload(file, "stores", true, { type: "store-logo", store: storeSlug }), onChange: (url) => onChange({ storeLogoURL: url }), helperText: "Square image, min 200\u00D7200px" }), _jsx(ImageUpload, { label: "Store Banner", currentImage: values.storeBannerURL, onUpload: (file) => upload(file, "stores", true, { type: "store-banner", store: storeSlug }), onChange: (url) => onChange({ storeBannerURL: url }), helperText: "Recommended: 1200\u00D7300px" })] })] })),
42
68
  },
43
69
  {
44
70
  label: "Policies",
71
+ fields: ["returnPolicy", "shippingPolicy"],
45
72
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Policies" }), _jsx(FormField, { name: "returnPolicy", label: "Return Policy", type: "textarea", value: values.returnPolicy ?? "", onChange: (v) => onChange({ returnPolicy: v }), placeholder: "Describe your return policy\u2026", disabled: busy }), _jsx(FormField, { name: "shippingPolicy", label: "Shipping Policy", type: "textarea", value: values.shippingPolicy ?? "", onChange: (v) => onChange({ shippingPolicy: v }), placeholder: "How and when you ship orders\u2026", disabled: busy })] })),
46
73
  },
47
74
  {
48
75
  label: "Contact & Visibility",
76
+ fields: ["website", "location", "socialLinks", "isPublic", "isVacationMode", "vacationMessage"],
49
77
  render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Contact & Social" }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "website", label: "Website", type: "text", value: values.website ?? "", onChange: (v) => onChange({ website: v }), placeholder: "https://example.com", disabled: busy }), _jsx(FormField, { name: "location", label: "Location", type: "text", value: values.location ?? "", onChange: (v) => onChange({ location: v }), placeholder: "e.g. Mumbai, India", disabled: busy })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "twitter", label: "Twitter / X URL", type: "text", value: values.socialLinks?.twitter ?? "", onChange: (v) => onChange({ socialLinks: { ...values.socialLinks, twitter: v } }), placeholder: "https://twitter.com/...", disabled: busy }), _jsx(FormField, { name: "instagram", label: "Instagram URL", type: "text", value: values.socialLinks?.instagram ?? "", onChange: (v) => onChange({ socialLinks: { ...values.socialLinks, instagram: v } }), placeholder: "https://instagram.com/...", disabled: busy })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "facebook", label: "Facebook URL", type: "text", value: values.socialLinks?.facebook ?? "", onChange: (v) => onChange({ socialLinks: { ...values.socialLinks, facebook: v } }), placeholder: "https://facebook.com/...", disabled: busy }), _jsx(FormField, { name: "linkedin", label: "LinkedIn URL", type: "text", value: values.socialLinks?.linkedin ?? "", onChange: (v) => onChange({ socialLinks: { ...values.socialLinks, linkedin: v } }), placeholder: "https://linkedin.com/...", disabled: busy })] }), _jsx(Heading, { level: 4, className: "mt-4 mb-2", children: "Visibility" }), _jsx(Toggle, { checked: values.isPublic !== false, onChange: (checked) => onChange({ isPublic: checked }), label: "Make store public \u2014 visible to all buyers", disabled: busy }), values.isPublic === false && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Your store is hidden. Existing orders are unaffected." })), _jsx(Heading, { level: 4, className: "mt-4 mb-2", children: "Vacation Mode" }), _jsx(Toggle, { checked: !!values.isVacationMode, onChange: (checked) => onChange({ isVacationMode: checked }), label: "Enable vacation mode \u2014 your store will show a notice to buyers", disabled: busy }), values.isVacationMode && (_jsx(FormField, { name: "vacationMessage", label: "Vacation Message", type: "textarea", value: values.vacationMessage ?? "", onChange: (v) => onChange({ vacationMessage: v }), placeholder: "I'm away until\u2026 Orders placed now will ship when I return.", disabled: busy }))] })),
50
78
  },
51
79
  ];
80
+ const fieldToStepIndex = useMemo(() => {
81
+ const map = {};
82
+ steps.forEach((step, i) => {
83
+ step.fields?.forEach((field) => { map[field] = i; });
84
+ });
85
+ return map;
86
+ // eslint-disable-next-line react-hooks/exhaustive-deps
87
+ }, [steps.length]);
88
+ const wizardShellCtx = useMemo(() => ({ ...shellCtx, fieldToStepIndex, goToStep: (n) => setCurrentStep(n) }), [shellCtx, fieldToStepIndex, setCurrentStep]);
52
89
  return (_jsx(StackedViewShell, { portal: "seller", title: "Storefront Settings", sections: [
53
- _jsxs(Div, { children: [saved && (_jsx(Alert, { variant: "success", className: "mb-6", children: "Changes saved successfully." })), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handleSave, formId: "seller-storefront", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: "Save Changes", isLoading: busy })] }, "stepform"),
90
+ _jsxs(Div, { children: [saved && (_jsx(Alert, { variant: "success", className: "mb-6", children: "Changes saved successfully." })), _jsxs(FormShellContext.Provider, { value: wizardShellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handleSave, formId: "seller-storefront", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: "Save Changes", isLoading: busy })] })] }, "stepform"),
54
91
  ] }));
55
92
  }
@@ -9,7 +9,7 @@ export type { SellerPreOrdersViewProps } from "./SellerPreOrdersView";
9
9
  export { SellerPrizeDrawsView } from "./SellerPrizeDrawsView";
10
10
  export type { SellerPrizeDrawsViewProps } from "./SellerPrizeDrawsView";
11
11
  export { PrintCenterView } from "./PrintCenterView";
12
- export { SellerOrdersView } from "./SellerOrdersView";
12
+ export { SellerOrdersView, SellerOrderDetailPanel } from "./SellerOrdersView";
13
13
  export type { SellerOrdersViewProps } from "./SellerOrdersView";
14
14
  export { SellerOffersView } from "./SellerOffersView";
15
15
  export type { SellerOffersViewProps } from "./SellerOffersView";
@@ -23,7 +23,7 @@ export { SellerBidsView } from "./SellerBidsView";
23
23
  export type { SellerBidsViewProps } from "./SellerBidsView";
24
24
  export { SellersListView } from "./SellersListView";
25
25
  export type { SellersListViewProps } from "./SellersListView";
26
- export { SellerPayoutsView } from "./SellerPayoutsView";
26
+ export { SellerPayoutsView, SellerPayoutDetailContent } from "./SellerPayoutsView";
27
27
  export type { SellerPayoutsViewProps } from "./SellerPayoutsView";
28
28
  export { SellerPayoutStats } from "./SellerPayoutStats";
29
29
  export type { SellerPayoutStatsProps } from "./SellerPayoutStats";
@@ -4,14 +4,14 @@ export { SellerAuctionsView } from "./SellerAuctionsView";
4
4
  export { SellerPreOrdersView } from "./SellerPreOrdersView";
5
5
  export { SellerPrizeDrawsView } from "./SellerPrizeDrawsView";
6
6
  export { PrintCenterView } from "./PrintCenterView";
7
- export { SellerOrdersView } from "./SellerOrdersView";
7
+ export { SellerOrdersView, SellerOrderDetailPanel } from "./SellerOrdersView";
8
8
  export { SellerOffersView } from "./SellerOffersView";
9
9
  export { SellerOffersPanel } from "./SellerOffersPanel";
10
10
  export { SellerCouponsView } from "./SellerCouponsView";
11
11
  export { SellerCouponEditorView } from "./SellerCouponEditorView";
12
12
  export { SellerBidsView } from "./SellerBidsView";
13
13
  export { SellersListView } from "./SellersListView";
14
- export { SellerPayoutsView } from "./SellerPayoutsView";
14
+ export { SellerPayoutsView, SellerPayoutDetailContent } from "./SellerPayoutsView";
15
15
  export { SellerPayoutStats } from "./SellerPayoutStats";
16
16
  export { SellerPayoutHistoryTable } from "./SellerPayoutHistoryTable";
17
17
  export { SellerPayoutSettingsView } from "./SellerPayoutSettingsView";
@@ -1,4 +1,12 @@
1
1
  import { type ReactNode } from "react";
2
+ /**
3
+ * Carries the `schema` prop passed to `<FormShell>` down to any nested
4
+ * `<StepForm>` that doesn't declare its own `schema`/`StepDef.schema` —
5
+ * `FormShell` (chrome) has no form values of its own to validate, so it
6
+ * can't run `.safeParse()` itself; this is what makes the prop load-bearing
7
+ * instead of silently discarded.
8
+ */
9
+ export declare const FormSchemaContext: import("react").Context<import("zod").ZodTypeAny | undefined>;
2
10
  export interface FormShellSection {
3
11
  id: string;
4
12
  label: string;
@@ -20,6 +28,15 @@ export interface FormShellProps {
20
28
  publishLabel?: string;
21
29
  /** Override the entire bottom action bar. */
22
30
  renderBottomBar?: () => ReactNode;
31
+ /**
32
+ * Rendered above the default Save/Publish buttons in FormShell's own
33
+ * built-in footer (the `onSaveDraft`/`onPublish` branch, not used when
34
+ * `renderBottomBar` is supplied — that override owns its whole footer).
35
+ * Typically `<FormShellContext.Provider value={shellCtx}><FormErrorSummary /></FormShellContext.Provider>`
36
+ * — lets a caller show the shared live error summary beside the built-in
37
+ * buttons without having to reimplement the footer itself.
38
+ */
39
+ footerTopSlot?: ReactNode;
23
40
  /**
24
41
  * When provided, a 👁 Preview button appears in the top bar.
25
42
  * Clicking it replaces the form body with this read-only render.
@@ -33,9 +50,12 @@ export interface FormShellProps {
33
50
  */
34
51
  splitPreview?: boolean;
35
52
  /**
36
- * Optional Zod schema covering the form's fields. Currently informational
37
- * (consumed by the caller's submit handler); `audit-form-schema` requires
38
- * every callsite to declare one so the validation contract is explicit.
53
+ * Zod schema covering the form's fields. `FormShell` has no form values of
54
+ * its own, so it can't `.safeParse()` this directly — instead it provides
55
+ * the schema via `FormSchemaContext` to any nested `<StepForm>` that
56
+ * doesn't declare its own `schema`/`StepDef.schema`, so existing
57
+ * `<FormShell schema={x}>` call sites start validating with zero changes.
58
+ * `audit-form-schema` requires every callsite to declare one.
39
59
  */
40
60
  schema?: import("zod").ZodTypeAny;
41
61
  children: ReactNode;
@@ -46,4 +66,4 @@ export interface UseFormShellResult {
46
66
  markClean: () => void;
47
67
  }
48
68
  export declare function useFormShell(): UseFormShellResult;
49
- export declare function FormShell({ isOpen, onClose, title, breadcrumb, schema: _schema, isDirty, isLoading, sections, onSaveDraft, onPublish, saveLabel, publishLabel, renderBottomBar, previewSlot, splitPreview: splitPreviewProp, children, }: FormShellProps): import("react").JSX.Element | null;
69
+ export declare function FormShell({ isOpen, onClose, title, breadcrumb, schema, isDirty, isLoading, sections, onSaveDraft, onPublish, saveLabel, publishLabel, renderBottomBar, footerTopSlot, previewSlot, splitPreview: splitPreviewProp, children, }: FormShellProps): import("react").JSX.Element | null;
@@ -1,12 +1,20 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { createContext, useCallback, useEffect, useRef, useState } from "react";
4
4
  import { X, AlertTriangle, Eye, ArrowLeft } from "lucide-react";
5
5
  import { Button } from "../../ui/components/Button";
6
6
  import { IconButton } from "../../ui/components/IconButton";
7
7
  import { classNames } from "../../ui/style.helper";
8
8
  import { FORM_ACTION_META, FORM_ACTION_ID } from "../products/constants/action-defs";
9
9
  import { Div, Row, Span, Stack, Text } from "../../ui";
10
+ /**
11
+ * Carries the `schema` prop passed to `<FormShell>` down to any nested
12
+ * `<StepForm>` that doesn't declare its own `schema`/`StepDef.schema` —
13
+ * `FormShell` (chrome) has no form values of its own to validate, so it
14
+ * can't run `.safeParse()` itself; this is what makes the prop load-bearing
15
+ * instead of silently discarded.
16
+ */
17
+ export const FormSchemaContext = createContext(undefined);
10
18
  const __P = {
11
19
  p4: "p-[var(--appkit-space-4)]",
12
20
  };
@@ -24,9 +32,7 @@ export function useFormShell() {
24
32
  };
25
33
  }
26
34
  const FOCUSABLE = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
27
- export function FormShell({ isOpen, onClose, title, breadcrumb,
28
- // schema is consumed by callers (audit-form-schema asserts presence at callsites)
29
- schema: _schema, isDirty = false, isLoading = false, sections, onSaveDraft, onPublish, saveLabel = FORM_ACTION_META[FORM_ACTION_ID.SAVE_DRAFT].label, publishLabel = FORM_ACTION_META[FORM_ACTION_ID.PUBLISH].label, renderBottomBar, previewSlot, splitPreview: splitPreviewProp, children, }) {
35
+ export function FormShell({ isOpen, onClose, title, breadcrumb, schema, isDirty = false, isLoading = false, sections, onSaveDraft, onPublish, saveLabel = FORM_ACTION_META[FORM_ACTION_ID.SAVE_DRAFT].label, publishLabel = FORM_ACTION_META[FORM_ACTION_ID.PUBLISH].label, renderBottomBar, footerTopSlot, previewSlot, splitPreview: splitPreviewProp, children, }) {
30
36
  // W1-37: auto-enable splitPreview when a previewSlot is provided. Callers
31
37
  // can still pass splitPreview={false} explicitly to opt out.
32
38
  const splitPreview = splitPreviewProp ?? Boolean(previewSlot);
@@ -116,8 +122,8 @@ schema: _schema, isDirty = false, isLoading = false, sections, onSaveDraft, onPu
116
122
  };
117
123
  if (!isOpen)
118
124
  return null;
119
- return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-sm", className: "fixed inset-0 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs(Div, { layout: "flex", ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 bottom-0 left-0 right-0 lg:left-[10%] lg:right-[10%] [flex-direction:column] bg-[var(--appkit-color-surface)]", shadow: "2xl", style: { zIndex: "var(--appkit-z-modal)" }, children: [_jsxs(Row, { gap: "sm", paddingX: "x-5", className: "flex-shrink-0 sticky top-0 z-10 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", padding: "y-sm", children: [previewMode ? (_jsxs(Button, { variant: "ghost", type: "button", onClick: () => setPreviewMode(false), "aria-label": "Back to edit", gap: "xs", rounded: "lg", textSize: "sm", textColor: "muted", className: "flex-shrink-0", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), _jsx(Span, { className: "hidden sm:inline", children: "Back to Edit" })] })) : (_jsx(IconButton, { type: "button", onClick: attemptClose, "aria-label": "Close", variant: "ghost", rounded: "lg", className: "text-[var(--appkit-color-text-muted)] flex-shrink-0", children: _jsx(X, { className: "w-5 h-5" }) })), _jsxs(Div, { className: "flex-1 min-w-0", children: [breadcrumb && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)] truncate mb-0.5", size: "xs", children: breadcrumb })), _jsx(Text, { className: "text-[var(--appkit-color-text)] truncate", size: "sm", weight: "semibold", children: previewMode ? `Preview — ${title}` : title })] }), _jsx(Row, { gap: "xs", className: "flex-shrink-0", children: previewMode ? null : (_jsxs(_Fragment, { children: [previewSlot && (_jsxs(Button, { variant: "outline", type: "button", onClick: () => setPreviewMode(true), gap: "xs", rounded: "lg", paddingX: "sm", textSize: "sm", textColor: "muted", children: [_jsx(Eye, { className: "w-4 h-4" }), _jsx(Span, { className: "hidden sm:inline", children: "Preview" })] })), onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsx(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: publishLabel }))] })) })] }), _jsxs(Div, { layout: "flex", className: `flex-1 ${__O.hidden}`, children: [sections && sections.length > 0 && !previewMode && (_jsx("nav", { "aria-label": "Form sections", className: "hidden lg:flex flex-col flex-shrink-0 w-48 border-r border-[var(--appkit-color-border)] py-[var(--appkit-space-4)] px-[var(--appkit-space-3)] gap-[var(--appkit-space-1)] overflow-y-auto", children: sections.map((sec) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => scrollToSection(sec.id), rounded: "lg", paddingX: "sm", paddingY: "xs", textSize: "sm", textColor: "muted", justify: "start", className: "text-left", children: sec.label }, sec.id))) })), sections && sections.length > 0 && !previewMode && (_jsx(Row, { gap: "px", className: `lg:hidden fixed top-[var(--form-shell-topbar-h,57px)] left-0 right-0 z-10 ${__O.xAuto} px-[1.25rem] bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]`, padding: "y-xs", children: sections.map((sec) => (_jsx(Button, { variant: "outline", type: "button", onClick: () => scrollToSection(sec.id), rounded: "full", paddingX: "sm", paddingY: "xs", textSize: "xs", textColor: "muted", className: "flex-shrink-0 hover:border-primary hover:text-primary whitespace-nowrap", children: sec.label }, sec.id))) })), _jsx(Div, { ref: bodyRef, className: classNames("flex-1 overflow-y-auto", sections && sections.length > 0 && !previewMode ? "pt-[var(--appkit-space-0)] lg:pt-[var(--appkit-space-0)]" : ""), children: previewMode && previewSlot ? (_jsxs(_Fragment, { children: [_jsxs(Row, { textSize: "sm", gap: "xs", className: "sticky top-0 z-10 bg-[var(--appkit-color-warning-surface)] border-b border-[var(--appkit-color-warning)] text-[var(--appkit-color-warning-text,var(--appkit-color-warning))]", paddingY: "y-xs", paddingX: "x-md", children: [_jsx(Eye, { className: "w-4 h-4 flex-shrink-0" }), _jsx(Span, { children: "Preview \u2014 not visible to buyers until published" })] }), _jsx(Div, { padding: "y-md", children: previewSlot() })] })) : splitPreview && previewSlot ? (
125
+ return (_jsxs(FormSchemaContext.Provider, { value: schema, children: [_jsx(Div, { surface: "overlay-sm", className: "fixed inset-0 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs(Div, { layout: "flex", ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 bottom-0 left-0 right-0 lg:left-[10%] lg:right-[10%] [flex-direction:column] bg-[var(--appkit-color-surface)]", shadow: "2xl", style: { zIndex: "var(--appkit-z-modal)" }, children: [_jsxs(Row, { gap: "sm", paddingX: "x-5", className: "flex-shrink-0 sticky top-0 z-10 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", padding: "y-sm", children: [previewMode ? (_jsxs(Button, { variant: "ghost", type: "button", onClick: () => setPreviewMode(false), "aria-label": "Back to edit", gap: "xs", rounded: "lg", textSize: "sm", textColor: "muted", className: "flex-shrink-0", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), _jsx(Span, { className: "hidden sm:inline", children: "Back to Edit" })] })) : (_jsx(IconButton, { type: "button", onClick: attemptClose, "aria-label": "Close", variant: "ghost", rounded: "lg", className: "text-[var(--appkit-color-text-muted)] flex-shrink-0", children: _jsx(X, { className: "w-5 h-5" }) })), _jsxs(Div, { className: "flex-1 min-w-0", children: [breadcrumb && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)] truncate mb-0.5", size: "xs", children: breadcrumb })), _jsx(Text, { className: "text-[var(--appkit-color-text)] truncate", size: "sm", weight: "semibold", children: previewMode ? `Preview — ${title}` : title })] }), _jsx(Row, { gap: "xs", className: "flex-shrink-0", children: previewMode ? null : (_jsxs(_Fragment, { children: [previewSlot && (_jsxs(Button, { variant: "outline", type: "button", onClick: () => setPreviewMode(true), gap: "xs", rounded: "lg", paddingX: "sm", textSize: "sm", textColor: "muted", children: [_jsx(Eye, { className: "w-4 h-4" }), _jsx(Span, { className: "hidden sm:inline", children: "Preview" })] })), onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsx(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: publishLabel }))] })) })] }), _jsxs(Div, { layout: "flex", className: `flex-1 ${__O.hidden}`, children: [sections && sections.length > 0 && !previewMode && (_jsx("nav", { "aria-label": "Form sections", className: "hidden lg:flex flex-col flex-shrink-0 w-48 border-r border-[var(--appkit-color-border)] py-[var(--appkit-space-4)] px-[var(--appkit-space-3)] gap-[var(--appkit-space-1)] overflow-y-auto", children: sections.map((sec) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => scrollToSection(sec.id), rounded: "lg", paddingX: "sm", paddingY: "xs", textSize: "sm", textColor: "muted", justify: "start", className: "text-left", children: sec.label }, sec.id))) })), sections && sections.length > 0 && !previewMode && (_jsx(Row, { gap: "px", className: `lg:hidden fixed top-[var(--form-shell-topbar-h,57px)] left-0 right-0 z-10 ${__O.xAuto} px-[1.25rem] bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]`, padding: "y-xs", children: sections.map((sec) => (_jsx(Button, { variant: "outline", type: "button", onClick: () => scrollToSection(sec.id), rounded: "full", paddingX: "sm", paddingY: "xs", textSize: "xs", textColor: "muted", className: "flex-shrink-0 hover:border-primary hover:text-primary whitespace-nowrap", children: sec.label }, sec.id))) })), _jsx(Div, { ref: bodyRef, className: classNames("flex-1 overflow-y-auto", sections && sections.length > 0 && !previewMode ? "pt-[var(--appkit-space-0)] lg:pt-[var(--appkit-space-0)]" : ""), children: previewMode && previewSlot ? (_jsxs(_Fragment, { children: [_jsxs(Row, { textSize: "sm", gap: "xs", className: "sticky top-0 z-10 bg-[var(--appkit-color-warning-surface)] border-b border-[var(--appkit-color-warning)] text-[var(--appkit-color-warning-text,var(--appkit-color-warning))]", paddingY: "y-xs", paddingX: "x-md", children: [_jsx(Eye, { className: "w-4 h-4 flex-shrink-0" }), _jsx(Span, { children: "Preview \u2014 not visible to buyers until published" })] }), _jsx(Div, { padding: "y-md", children: previewSlot() })] })) : splitPreview && previewSlot ? (
120
126
  // S-STORE-3-D — desktop 60/40 split: form left, preview right.
121
127
  // Below `lg`, falls back to single-column form (preview-as-modal via existing toggle).
122
- _jsxs(Div, { className: "lg:grid lg:grid-cols-[3fr_2fr] lg:gap-[1.5rem] lg:px-[1.5rem] lg:py-[1.5rem] lg:max-w-[1400px] lg:mx-auto", children: [_jsx(Div, { className: "max-w-3xl mx-auto px-[1.25rem] sm:px-[1.5rem] lg:max-w-none lg:px-[0] lg:py-[0]", padding: "y-lg", children: children }), _jsxs(Div, { className: `hidden lg:block sticky lg:top-4 lg:self-start lg:max-h-[calc(100vh-6rem)] ${__O.yAuto} border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-raised)] ${__P.p4}`, rounded: "lg", children: [_jsxs(Row, { textWeight: "semibold", textSize: "xs", gap: "xs", className: "mb-3 uppercase tracking-wide text-[var(--appkit-color-text-muted)]", children: [_jsx(Eye, { className: "w-3.5 h-3.5" }), _jsx(Span, { children: "Live preview" })] }), previewSlot()] })] })) : (_jsx(Div, { className: "max-w-3xl mx-auto px-[1.25rem] sm:px-[1.5rem]", padding: "y-lg", children: children })) })] }), !previewMode && renderBottomBar ? (renderBottomBar()) : !previewMode && (onSaveDraft || onPublish) ? (_jsxs(Row, { justify: "between", paddingX: "x-5", className: "flex-shrink-0 sticky bottom-0 z-10 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", padding: "y-sm", style: { paddingBottom: "calc(var(--appkit-space-3) + var(--keyboard-inset-height, 0px))" }, children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: attemptClose, disabled: isLoading, children: FORM_ACTION_META[FORM_ACTION_ID.DISCARD].label }), _jsxs(Row, { gap: "xs", children: [onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsxs(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: [publishLabel, " \u2192"] }))] })] })) : null] }), showUnsaved && (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-lg", className: "fixed inset-0", style: { zIndex: "var(--appkit-z-confirm)" }, onClick: () => setShowUnsaved(false) }), _jsxs(Div, { className: "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] max-w-sm bg-[var(--appkit-color-surface)]", shadow: "2xl", rounded: "xl", padding: "lg", style: { zIndex: "var(--appkit-z-confirm)" }, children: [_jsxs(Row, { gap: "sm", align: "start", className: "mb-4", children: [_jsx(Span, { layout: "flex-center", className: "flex-shrink-0 w-10 h-10 bg-[var(--appkit-color-warning-surface)]", rounded: "full", children: _jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--appkit-color-warning)]" }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", weight: "semibold", children: "Unsaved changes" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "You have unsaved changes. Leave without saving?" })] })] }), _jsxs(Row, { gap: "xs", justify: "end", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowUnsaved(false), children: "Stay" }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => { setShowUnsaved(false); onClose(); }, children: "Leave" })] })] })] }))] }));
128
+ _jsxs(Div, { className: "lg:grid lg:grid-cols-[3fr_2fr] lg:gap-[1.5rem] lg:px-[1.5rem] lg:py-[1.5rem] lg:max-w-[1400px] lg:mx-auto", children: [_jsx(Div, { className: "max-w-3xl mx-auto px-[1.25rem] sm:px-[1.5rem] lg:max-w-none lg:px-[0] lg:py-[0]", padding: "y-lg", children: children }), _jsxs(Div, { className: `hidden lg:block sticky lg:top-4 lg:self-start lg:max-h-[calc(100vh-6rem)] ${__O.yAuto} border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-raised)] ${__P.p4}`, rounded: "lg", children: [_jsxs(Row, { textWeight: "semibold", textSize: "xs", gap: "xs", className: "mb-3 uppercase tracking-wide text-[var(--appkit-color-text-muted)]", children: [_jsx(Eye, { className: "w-3.5 h-3.5" }), _jsx(Span, { children: "Live preview" })] }), previewSlot()] })] })) : (_jsx(Div, { className: "max-w-3xl mx-auto px-[1.25rem] sm:px-[1.5rem]", padding: "y-lg", children: children })) })] }), !previewMode && renderBottomBar ? (renderBottomBar()) : !previewMode && (onSaveDraft || onPublish) ? (_jsxs(Stack, { gap: "none", className: "flex-shrink-0 sticky bottom-0 z-10 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [footerTopSlot && (_jsx(Div, { paddingX: "x-5", padding: "t-sm", children: footerTopSlot })), _jsxs(Row, { justify: "between", paddingX: "x-5", padding: "y-sm", style: { paddingBottom: "calc(var(--appkit-space-3) + var(--keyboard-inset-height, 0px))" }, children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: attemptClose, disabled: isLoading, children: FORM_ACTION_META[FORM_ACTION_ID.DISCARD].label }), _jsxs(Row, { gap: "xs", children: [onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsxs(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: [publishLabel, " \u2192"] }))] })] })] })) : null] }), showUnsaved && (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-lg", className: "fixed inset-0", style: { zIndex: "var(--appkit-z-confirm)" }, onClick: () => setShowUnsaved(false) }), _jsxs(Div, { className: "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] max-w-sm bg-[var(--appkit-color-surface)]", shadow: "2xl", rounded: "xl", padding: "lg", style: { zIndex: "var(--appkit-z-confirm)" }, children: [_jsxs(Row, { gap: "sm", align: "start", className: "mb-4", children: [_jsx(Span, { layout: "flex-center", className: "flex-shrink-0 w-10 h-10 bg-[var(--appkit-color-warning-surface)]", rounded: "full", children: _jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--appkit-color-warning)]" }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", weight: "semibold", children: "Unsaved changes" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "You have unsaved changes. Leave without saving?" })] })] }), _jsxs(Row, { gap: "xs", justify: "end", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowUnsaved(false), children: "Stay" }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => { setShowUnsaved(false); onClose(); }, children: "Leave" })] })] })] }))] }));
123
129
  }
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { Button } from "../../ui/components/Button";
6
6
  import { IconButton } from "../../ui/components/IconButton";
7
7
  import { FormField } from "../../ui/components/FormField";
8
8
  import { Toggle } from "../../ui/components/Toggle";
9
9
  import { Div, Form, Row, Stack, Text } from "../../ui";
10
+ import { FormShellContext, FormErrorSummary } from "../../ui/forms";
10
11
  import { useHandMode } from "../../_internal/client/hand-mode";
11
12
  function initValues(fields, defaults = {}) {
12
13
  const out = {};
@@ -67,6 +68,37 @@ export function QuickFormDrawer({ isOpen, onClose, title, fields, defaultValues,
67
68
  setErrors(errs);
68
69
  return Object.keys(errs).length === 0;
69
70
  };
71
+ // Live-derived (not state — a pure function of the current values, so it
72
+ // never needs a separate effect) validation for the error summary below.
73
+ // Deliberately kept SEPARATE from the `errors` state above: `errors` only
74
+ // populates on submit and gates each field's inline error display, which
75
+ // must stay submit-gated so an untouched, freshly-opened drawer doesn't
76
+ // show every required field as invalid immediately. The summary is meant
77
+ // to be live per the shared error-summary requirement; the per-field
78
+ // inline errors are not.
79
+ const liveErrors = (() => {
80
+ if (schema) {
81
+ const parsed = schema.safeParse(values);
82
+ if (parsed.success)
83
+ return {};
84
+ const errs = {};
85
+ for (const issue of parsed.error.issues) {
86
+ const path = issue.path[0];
87
+ if (typeof path === "string" && !errs[path])
88
+ errs[path] = issue.message;
89
+ }
90
+ return errs;
91
+ }
92
+ const errs = {};
93
+ for (const f of fields) {
94
+ if (f.required) {
95
+ const v = values[f.name];
96
+ if (v === undefined || v === null || v === "")
97
+ errs[f.name] = `${f.label} is required`;
98
+ }
99
+ }
100
+ return errs;
101
+ })();
70
102
  const handleSubmit = useCallback(async (e) => {
71
103
  e?.preventDefault();
72
104
  if (!validate())
@@ -121,18 +153,41 @@ export function QuickFormDrawer({ isOpen, onClose, title, fields, defaultValues,
121
153
  document.addEventListener("keydown", handle);
122
154
  return () => document.removeEventListener("keydown", handle);
123
155
  }, [isOpen, handleClose]);
156
+ // Synthesize a FormShellContextValue from `liveErrors` (not the submit-gated
157
+ // `errors` state) so <FormErrorSummary> — the shared live-error-summary
158
+ // primitive — can be reused here without migrating this drawer's
159
+ // already-working schema/errors mechanism onto useFormShellState. Kept
160
+ // separate from `errors` deliberately: this context only backs the
161
+ // summary, never the per-field inline `error={errors[field.name]}` props
162
+ // above, which must stay submit-gated.
163
+ const shellCtx = useMemo(() => ({
164
+ errors: liveErrors,
165
+ touched: {},
166
+ setFieldError: () => { },
167
+ setFieldTouched: () => { },
168
+ clearFieldError: () => { },
169
+ steps: [],
170
+ currentStep: 0,
171
+ goToStep: () => { },
172
+ nextStep: () => { },
173
+ prevStep: () => { },
174
+ isPublishReady: Object.keys(liveErrors).length === 0,
175
+ isDirty: false,
176
+ isSubmitting: submitting,
177
+ stepErrorCounts: [],
178
+ }), [liveErrors, submitting]);
124
179
  if (!isOpen)
125
180
  return null;
126
181
  const busy = submitting || isLoading;
127
- return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 backdrop-blur-[2px] [z-index:calc(var(--appkit-z-modal)+1)]", "aria-hidden": "true", onClick: handleClose }), _jsxs(Div, { ref: drawerRef, role: "dialog", "aria-modal": "true", "aria-label": title, layout: "flex-col", shadow: "2xl", className: `fixed inset-y-0 ${hand === "left" ? "left-0" : "right-0"} bg-[var(--appkit-color-surface)] w-full lg:w-[40%] [z-index:calc(var(--appkit-z-modal)+2)]`, children: [_jsxs(Row, { className: "flex-shrink-0 border-b border-[var(--appkit-color-border)]", padding: "md", align: "center", gap: "3", reverse: hand === "left", children: [_jsx(Text, { className: "flex-1 text-[var(--appkit-color-text)]", size: "base", weight: "semibold", children: title }), _jsx(IconButton, { type: "button", onClick: handleClose, "aria-label": "Close", variant: "ghost", rounded: "lg", className: "text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)]", children: _jsx(X, { className: "w-5 h-5" }) })] }), _jsxs(Form, { className: "flex-1", overflow: "y-auto", paddingX: "x-md", paddingY: "y-md-lg", spacing: "md", onSubmit: handleSubmit, noValidate: true, children: [fields.map((field) => {
128
- const value = values[field.name];
129
- if (field.type === "toggle") {
130
- return (_jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { checked: !!value, onChange: (checked) => set(field.name, checked), label: field.label }), field.helperText && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: field.helperText }))] }, field.name));
131
- }
132
- return (_jsxs(Div, { children: [_jsx(FormField, { name: field.name, label: field.label, type: field.type === "textarea"
133
- ? "textarea"
134
- : field.type === "select"
135
- ? "select"
136
- : "text", value: String(value ?? ""), onChange: (v) => set(field.name, field.type === "number" ? Number(v) : v), options: field.options, placeholder: field.placeholder, disabled: busy, error: errors[field.name] }), field.helperText && !errors[field.name] && (_jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)]", size: "xs", children: field.helperText }))] }, field.name));
137
- }), renderExtra?.(values, set)] }), _jsxs(Row, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-bg)]", padding: "md", align: "center", justify: "between", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: handleClose, disabled: busy, children: cancelLabel }), _jsxs(Button, { variant: "primary", size: "sm", onClick: () => void handleSubmit(), disabled: busy, isLoading: busy, children: [submitLabel, " \u2192"] })] })] })] }));
182
+ return (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 backdrop-blur-[2px] [z-index:calc(var(--appkit-z-modal)+1)]", "aria-hidden": "true", onClick: handleClose }), _jsxs(Div, { ref: drawerRef, role: "dialog", "aria-modal": "true", "aria-label": title, layout: "flex-col", shadow: "2xl", className: `fixed inset-y-0 ${hand === "left" ? "left-0" : "right-0"} bg-[var(--appkit-color-surface)] w-full lg:w-[40%] [z-index:calc(var(--appkit-z-modal)+2)]`, children: [_jsxs(Row, { className: "flex-shrink-0 border-b border-[var(--appkit-color-border)]", padding: "md", align: "center", gap: "3", reverse: hand === "left", children: [_jsx(Text, { className: "flex-1 text-[var(--appkit-color-text)]", size: "base", weight: "semibold", children: title }), _jsx(IconButton, { type: "button", onClick: handleClose, "aria-label": "Close", variant: "ghost", rounded: "lg", className: "text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)]", children: _jsx(X, { className: "w-5 h-5" }) })] }), _jsxs(Form, { className: "flex-1", overflow: "y-auto", paddingX: "x-md", paddingY: "y-md-lg", spacing: "md", onSubmit: handleSubmit, noValidate: true, children: [fields.map((field) => {
183
+ const value = values[field.name];
184
+ if (field.type === "toggle") {
185
+ return (_jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { checked: !!value, onChange: (checked) => set(field.name, checked), label: field.label }), field.helperText && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: field.helperText }))] }, field.name));
186
+ }
187
+ return (_jsxs(Div, { children: [_jsx(FormField, { name: field.name, label: field.label, type: field.type === "textarea"
188
+ ? "textarea"
189
+ : field.type === "select"
190
+ ? "select"
191
+ : "text", value: String(value ?? ""), onChange: (v) => set(field.name, field.type === "number" ? Number(v) : v), options: field.options, placeholder: field.placeholder, disabled: busy, error: errors[field.name] }), field.helperText && !errors[field.name] && (_jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)]", size: "xs", children: field.helperText }))] }, field.name));
192
+ }), renderExtra?.(values, set)] }), _jsxs(Div, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-bg)]", children: [_jsx(Div, { paddingX: "x-md", padding: "t-sm", children: _jsx(FormErrorSummary, {}) }), _jsxs(Row, { padding: "md", align: "center", justify: "between", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: handleClose, disabled: busy, children: cancelLabel }), _jsxs(Button, { variant: "primary", size: "sm", onClick: () => void handleSubmit(), disabled: busy, isLoading: busy, children: [submitLabel, " \u2192"] })] })] })] })] }) }));
138
193
  }
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { JsonValue } from "@mohasinac/appkit/client";
3
+ import type { ZodTypeAny } from "zod";
3
4
  export interface StepDef<T extends object = Record<string, JsonValue>> {
4
5
  label: string;
5
6
  render: (props: {
@@ -9,6 +10,19 @@ export interface StepDef<T extends object = Record<string, JsonValue>> {
9
10
  }) => ReactNode;
10
11
  /** Returns an error message string if invalid, or null/undefined if valid. */
11
12
  validate?: (values: T) => string | null | undefined;
13
+ /**
14
+ * Schema field names this step owns — drives error→step attribution
15
+ * (surfaced to the caller via `onValidationChange`'s `fieldToStepIndex`,
16
+ * consumed by `<FormErrorSummary>`).
17
+ */
18
+ fields?: string[];
19
+ /**
20
+ * Per-step Zod schema. Runs live (on every value change, not just on
21
+ * Next/Publish) via `onValidationChange`. Falls back to the whole-form
22
+ * `schema` prop, then to a schema inherited from an ancestor `FormShell`
23
+ * via `FormSchemaContext`, when omitted.
24
+ */
25
+ schema?: ZodTypeAny;
12
26
  }
13
27
  export interface StepFormProps<T extends object = Record<string, JsonValue>> {
14
28
  steps: StepDef<T>[];
@@ -26,6 +40,21 @@ export interface StepFormProps<T extends object = Record<string, JsonValue>> {
26
40
  hideActions?: boolean;
27
41
  /** Per-step error flags — when true, step button shows a red error badge */
28
42
  stepErrors?: boolean[];
43
+ /**
44
+ * Whole-form Zod schema, used by any step that doesn't declare its own
45
+ * `StepDef.schema`. Falls back to an ancestor `FormShell`'s schema (via
46
+ * `FormSchemaContext`) when omitted here too.
47
+ */
48
+ schema?: ZodTypeAny;
49
+ /**
50
+ * Fired whenever live validation recomputes — the current field-name→
51
+ * message map plus a field-name→step-index map derived from `steps[].fields`.
52
+ * The caller (which owns the single `FormShellContext.Provider` for the
53
+ * surrounding form) composes these into its own context value so
54
+ * `<FormErrorSummary>` and per-field inline errors both work, instead of
55
+ * `StepForm` mounting a second, potentially-shadowing provider itself.
56
+ */
57
+ onValidationChange?: (fieldErrors: Record<string, string>, fieldToStepIndex: Record<string, number>) => void;
29
58
  }
30
59
  export interface StepFormActionsProps {
31
60
  currentStep: number;
@@ -48,4 +77,4 @@ export declare function StepIndicator({ steps, currentStep, onStepClick, stepErr
48
77
  /** True for each step that has a validation error — renders a red dot badge */
49
78
  stepErrors?: boolean[];
50
79
  }): import("react").JSX.Element;
51
- export declare function StepForm<T extends object = Record<string, JsonValue>>({ steps, values, onChange, onComplete, completeLabel, isLoading, formId, currentStep, onStepChange, hideActions, stepErrors, }: StepFormProps<T>): import("react").JSX.Element;
80
+ export declare function StepForm<T extends object = Record<string, JsonValue>>({ steps, values, onChange, onComplete, completeLabel, isLoading, formId, currentStep, onStepChange, hideActions, stepErrors, schema, onValidationChange, }: StepFormProps<T>): import("react").JSX.Element;