@mohasinac/appkit 4.59.0 → 4.60.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 (531) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
  3. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
  4. package/dist/_internal/server/features/brands/actions.js +78 -4
  5. package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
  6. package/dist/_internal/server/features/checkout/actions.js +668 -183
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
  8. package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
  9. package/dist/_internal/server/features/gateway-events/index.js +10 -0
  10. package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
  11. package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
  12. package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
  13. package/dist/_internal/server/features/gateway-events/redact.js +151 -0
  14. package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
  15. package/dist/_internal/server/features/grievance/actions.js +173 -0
  16. package/dist/_internal/server/features/grievance/index.d.ts +8 -0
  17. package/dist/_internal/server/features/grievance/index.js +8 -0
  18. package/dist/_internal/server/features/grouped/data.js +28 -3
  19. package/dist/_internal/server/features/lottery/actions.js +99 -4
  20. package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
  21. package/dist/_internal/server/features/order-completion/actions.js +336 -0
  22. package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
  23. package/dist/_internal/server/features/order-completion/index.js +7 -0
  24. package/dist/_internal/server/features/orders/actions.d.ts +13 -0
  25. package/dist/_internal/server/features/orders/actions.js +56 -2
  26. package/dist/_internal/server/features/orders/adapters.js +26 -0
  27. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  29. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  30. package/dist/_internal/server/features/orders/index.js +1 -1
  31. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  32. package/dist/_internal/server/features/orders/service.js +74 -0
  33. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  35. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  36. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  37. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  38. package/dist/_internal/server/features/payouts/actions.js +84 -10
  39. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  40. package/dist/_internal/server/features/points/adjust.js +121 -0
  41. package/dist/_internal/server/features/points/award.d.ts +65 -0
  42. package/dist/_internal/server/features/points/award.js +145 -0
  43. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  44. package/dist/_internal/server/features/price-index/market.js +54 -1
  45. package/dist/_internal/server/features/products/data.js +40 -22
  46. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  47. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  48. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  49. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  50. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  51. package/dist/_internal/server/features/refunds/actions.js +259 -17
  52. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  53. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  54. package/dist/_internal/server/features/reviews/actions.js +3 -2
  55. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  56. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  57. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  58. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  59. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  60. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  61. package/dist/_internal/server/features/stores/tabs.js +24 -4
  62. package/dist/_internal/server/functions/https.d.ts +31 -1
  63. package/dist/_internal/server/functions/https.js +52 -1
  64. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  65. package/dist/_internal/server/functions/scheduled.js +124 -1
  66. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  67. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  69. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  70. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  72. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  73. package/dist/_internal/server/jobs/core/index.js +2 -0
  74. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  76. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  78. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  80. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  81. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  82. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  84. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  86. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  87. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  88. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  90. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  92. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  93. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  95. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  96. package/dist/_internal/shared/actions/action-registry.js +71 -0
  97. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  99. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  100. package/dist/_internal/shared/checkout/lanes.js +46 -0
  101. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  102. package/dist/_internal/shared/checkout/order-math.js +251 -3
  103. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  104. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  105. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  106. package/dist/_internal/shared/features/auctions/config.js +30 -1
  107. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  108. package/dist/_internal/shared/features/brands/schema.js +18 -0
  109. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  111. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  112. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  113. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  114. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  115. package/dist/_internal/shared/fees/calculator.js +103 -5
  116. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  117. package/dist/_internal/shared/fees/india-states.js +190 -0
  118. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  119. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  120. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  121. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  122. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  123. package/dist/_internal/shared/shipping/quote.js +67 -0
  124. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  125. package/dist/_internal/shared/time/business-days.js +185 -0
  126. package/dist/cli/index.js +0 -2
  127. package/dist/client.d.ts +9 -4
  128. package/dist/client.js +26 -3
  129. package/dist/configs/tailwind.d.ts +0 -12
  130. package/dist/configs/tailwind.js +28 -15
  131. package/dist/constants/api-endpoints.d.ts +51 -9
  132. package/dist/constants/api-endpoints.js +23 -7
  133. package/dist/constants/index.d.ts +1 -1
  134. package/dist/constants/index.js +1 -1
  135. package/dist/contracts/index.d.ts +1 -1
  136. package/dist/contracts/payment.d.ts +131 -0
  137. package/dist/features/about/components/AboutView.js +1 -1
  138. package/dist/features/about/components/DeveloperView.js +5 -2
  139. package/dist/features/about/components/FAQPageView.js +2 -1
  140. package/dist/features/about/components/FeesView.js +2 -1
  141. package/dist/features/about/components/HelpPageView.js +2 -1
  142. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  143. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  144. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  145. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  146. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  147. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  148. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  149. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  150. package/dist/features/about/components/PolicyPageView.js +2 -1
  151. package/dist/features/about/components/ProseHero.d.ts +73 -0
  152. package/dist/features/about/components/ProseHero.js +18 -0
  153. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  154. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  155. package/dist/features/about/components/TrackOrderView.js +2 -1
  156. package/dist/features/about/components/UnauthorizedView.js +1 -1
  157. package/dist/features/about/index.d.ts +2 -0
  158. package/dist/features/about/index.js +5 -0
  159. package/dist/features/account/actions/address-actions.d.ts +26 -2
  160. package/dist/features/account/actions/address-actions.js +25 -1
  161. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  162. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  163. package/dist/features/account/components/UserOrdersView.js +17 -0
  164. package/dist/features/account/components/UserSidebar.js +1 -1
  165. package/dist/features/account/hooks/useNotifications.js +29 -0
  166. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  167. package/dist/features/admin/components/AdminBeysView.js +2 -2
  168. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  169. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  170. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  171. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  174. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  175. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  176. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  177. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  178. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  179. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  180. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  181. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  182. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  183. package/dist/features/admin/components/AdminPartsView.js +2 -2
  184. package/dist/features/admin/components/AdminProductsView.js +75 -4
  185. package/dist/features/admin/components/AdminSidebar.js +2 -2
  186. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  187. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  188. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  189. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  190. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  191. package/dist/features/admin/components/AdminUsersView.js +72 -1
  192. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  193. package/dist/features/admin/components/AdminViewCards.js +4 -4
  194. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  195. package/dist/features/admin/components/DataListingView.js +175 -14
  196. package/dist/features/admin/components/DataTable.d.ts +12 -1
  197. package/dist/features/admin/components/DataTable.js +3 -3
  198. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  199. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  200. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  201. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  202. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  203. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  204. package/dist/features/admin/components/index.d.ts +4 -0
  205. package/dist/features/admin/components/index.js +6 -0
  206. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  207. package/dist/features/admin/constants/filter-tabs.js +106 -0
  208. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  209. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  210. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  211. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  212. package/dist/features/admin/schemas/firestore.js +5 -1
  213. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  214. package/dist/features/auctions/actions/bid-actions.js +13 -1
  215. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  216. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  217. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  219. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  220. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  221. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  222. package/dist/features/audit-log/schemas/firestore.js +14 -0
  223. package/dist/features/auth/permissions/constants.d.ts +17 -1
  224. package/dist/features/auth/permissions/constants.js +18 -0
  225. package/dist/features/blog/components/BlogPostView.js +1 -1
  226. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  227. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  228. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  229. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  230. package/dist/features/categories/components/BundleDetailView.js +1 -1
  231. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  232. package/dist/features/categories/components/CategoryForm.js +12 -1
  233. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  234. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  235. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  236. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  237. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  238. package/dist/features/events/components/EventSaleBanner.js +1 -1
  239. package/dist/features/events/components/EventStatusBadge.js +1 -1
  240. package/dist/features/faq/components/FAQAccordion.js +2 -1
  241. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  242. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  243. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  244. package/dist/features/filters/FilterFacetSection.js +1 -1
  245. package/dist/features/filters/SwitchFilter.js +1 -1
  246. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  247. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  248. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  249. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  250. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  251. package/dist/features/gateway-events/schemas/index.js +79 -0
  252. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  253. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  254. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  255. package/dist/features/homepage/components/BrandsSection.js +1 -1
  256. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  257. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  258. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  259. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  260. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  261. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  262. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  264. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  265. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  266. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  267. package/dist/features/inventory/components/operator-message.js +41 -0
  268. package/dist/features/inventory/index.d.ts +13 -0
  269. package/dist/features/inventory/index.js +28 -0
  270. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  272. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  274. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  276. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  277. package/dist/features/inventory/schemas/firestore.js +159 -0
  278. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  279. package/dist/features/layout/AppLayoutShell.js +25 -12
  280. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  281. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  282. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  283. package/dist/features/layout/BottomNavbar.js +2 -2
  284. package/dist/features/media/MediaAvatar.js +1 -1
  285. package/dist/features/media/MediaImage.d.ts +24 -4
  286. package/dist/features/media/MediaImage.js +44 -4
  287. package/dist/features/media/MediaLightbox.js +1 -1
  288. package/dist/features/media/modals/ImageCropModal.js +1 -1
  289. package/dist/features/media/modals/ImageEditor.js +1 -1
  290. package/dist/features/media/upload/MediaUploadField.js +91 -3
  291. package/dist/features/obligations/constants/policies.d.ts +92 -0
  292. package/dist/features/obligations/constants/policies.js +378 -0
  293. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  294. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  295. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  296. package/dist/features/obligations/schemas/firestore.js +87 -0
  297. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  298. package/dist/features/orders/actions/order-actions.js +60 -3
  299. package/dist/features/orders/actions/refund-actions.js +70 -10
  300. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  301. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  302. package/dist/features/orders/components/OrdersList.js +2 -2
  303. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  304. package/dist/features/orders/components/index.d.ts +2 -0
  305. package/dist/features/orders/components/index.js +6 -0
  306. package/dist/features/orders/constants/transitions.d.ts +59 -0
  307. package/dist/features/orders/constants/transitions.js +104 -0
  308. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  309. package/dist/features/orders/repository/orders.repository.js +464 -21
  310. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  311. package/dist/features/orders/schemas/firestore.js +24 -0
  312. package/dist/features/orders/schemas/index.d.ts +61 -0
  313. package/dist/features/orders/schemas/index.js +22 -0
  314. package/dist/features/orders/types/index.d.ts +79 -0
  315. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  316. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  317. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  318. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  319. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  320. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  322. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  323. package/dist/features/payments/repository/payout.repository.js +0 -1
  324. package/dist/features/points/repository/points.repository.d.ts +122 -0
  325. package/dist/features/points/repository/points.repository.js +304 -1
  326. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  327. package/dist/features/points/schemas/adjust-form.js +63 -0
  328. package/dist/features/points/schemas/firestore.d.ts +108 -1
  329. package/dist/features/points/schemas/firestore.js +84 -0
  330. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  331. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  332. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  333. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  334. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  335. package/dist/features/price-index/market-stats.d.ts +82 -0
  336. package/dist/features/price-index/market-stats.js +230 -0
  337. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  338. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  339. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  340. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  341. package/dist/features/print/components/labels.d.ts +117 -0
  342. package/dist/features/print/components/labels.js +258 -0
  343. package/dist/features/print/components/labels.style.css +46 -0
  344. package/dist/features/print/index.d.ts +6 -0
  345. package/dist/features/print/index.js +16 -0
  346. package/dist/features/products/columns/productTableColumns.js +5 -4
  347. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  348. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  349. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  350. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  351. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  352. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  353. package/dist/features/products/components/ProductForm.js +34 -23
  354. package/dist/features/products/components/ProductGrid.js +86 -62
  355. package/dist/features/products/constants/card-badges.d.ts +97 -0
  356. package/dist/features/products/constants/card-badges.js +100 -0
  357. package/dist/features/products/repository/products.repository.js +34 -1
  358. package/dist/features/products/schemas/firestore.d.ts +46 -3
  359. package/dist/features/products/schemas/firestore.js +21 -0
  360. package/dist/features/promotions/components/PromotionsView.js +1 -1
  361. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  362. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  363. package/dist/features/promotions/schemas/firestore.js +30 -3
  364. package/dist/features/promotions/schemas/index.d.ts +6 -6
  365. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  366. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  367. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  368. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  369. package/dist/features/reviews/actions/review-actions.js +3 -2
  370. package/dist/features/reviews/components/ReviewModal.js +1 -1
  371. package/dist/features/reviews/components/ReviewsList.js +1 -1
  372. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  373. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  374. package/dist/features/scams/constants/scam-types.js +18 -0
  375. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  376. package/dist/features/scams/schemas/report-form.js +26 -1
  377. package/dist/features/search/components/Search.js +9 -9
  378. package/dist/features/search/components/SearchResultsClient.js +18 -3
  379. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  380. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  381. package/dist/features/seller/actions/offer-actions.js +16 -1
  382. package/dist/features/seller/actions/seller-actions.js +14 -3
  383. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  384. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  385. package/dist/features/seller/components/PrintCenterView.js +41 -30
  386. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  387. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  388. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  389. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  390. package/dist/features/seller/components/SellerProductShell.js +123 -13
  391. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  392. package/dist/features/seller/components/SellerSidebar.js +1 -1
  393. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  394. package/dist/features/seller/schemas/coupon-form.js +27 -0
  395. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  396. package/dist/features/seller/schemas/product-form.js +5 -0
  397. package/dist/features/shell/FormShell.js +2 -2
  398. package/dist/features/shell/QuickFormDrawer.js +14 -0
  399. package/dist/features/shell/SectionForm.d.ts +19 -0
  400. package/dist/features/shell/SectionForm.js +133 -9
  401. package/dist/features/shell/SectionForm.style.css +54 -0
  402. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  403. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  404. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  405. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  406. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  407. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  408. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  409. package/dist/features/stores/components/StoreHeader.js +1 -1
  410. package/dist/features/support/schemas/firestore.d.ts +73 -2
  411. package/dist/features/support/schemas/firestore.js +59 -0
  412. package/dist/index.d.ts +16 -24
  413. package/dist/index.js +38 -47
  414. package/dist/next/ErrorBoundary.js +1 -1
  415. package/dist/next/routing/route-map.d.ts +24 -0
  416. package/dist/next/routing/route-map.js +40 -0
  417. package/dist/providers/payment-manual/index.d.ts +15 -1
  418. package/dist/providers/payment-manual/index.js +37 -0
  419. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  420. package/dist/providers/payment-phonepe/index.js +36 -0
  421. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  422. package/dist/providers/payment-razorpay/index.js +99 -0
  423. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  424. package/dist/providers/shipping-shiprocket/index.js +18 -0
  425. package/dist/schemas/registry.d.ts +44 -11
  426. package/dist/server-entry.d.ts +2 -0
  427. package/dist/server-entry.js +27 -0
  428. package/dist/server.d.ts +42 -0
  429. package/dist/server.js +93 -0
  430. package/dist/styles.css +418 -149
  431. package/dist/tailwind-utilities.css +1 -1
  432. package/dist/tokens/themes/default-dark.js +15 -12
  433. package/dist/tokens/themes/default-light.js +14 -11
  434. package/dist/tokens/tokens.css +106 -23
  435. package/dist/ui/DataTable.style.css +4 -4
  436. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  437. package/dist/ui/columns/cell-renderers.js +26 -1
  438. package/dist/ui/columns/index.d.ts +1 -1
  439. package/dist/ui/columns/index.js +4 -1
  440. package/dist/ui/components/Badge.style.css +1 -1
  441. package/dist/ui/components/Breadcrumb.js +19 -1
  442. package/dist/ui/components/BulkActionBar.style.css +4 -4
  443. package/dist/ui/components/Button.style.css +1 -1
  444. package/dist/ui/components/Checkbox.style.css +1 -1
  445. package/dist/ui/components/FallbackShell.js +1 -1
  446. package/dist/ui/components/Form.d.ts +14 -1
  447. package/dist/ui/components/Form.js +2 -2
  448. package/dist/ui/components/FormField.js +18 -4
  449. package/dist/ui/components/FormField.style.css +43 -3
  450. package/dist/ui/components/GatedPrice.style.css +1 -1
  451. package/dist/ui/components/IconButton.style.css +1 -1
  452. package/dist/ui/components/Input.js +22 -1
  453. package/dist/ui/components/Input.style.css +38 -22
  454. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  455. package/dist/ui/components/ListingToolbar.js +1 -1
  456. package/dist/ui/components/PaginatedSelect.js +0 -0
  457. package/dist/ui/components/Radio.style.css +1 -1
  458. package/dist/ui/components/Repeater.d.ts +53 -0
  459. package/dist/ui/components/Repeater.js +7 -0
  460. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  461. package/dist/ui/components/RichTextEditor.js +23 -2
  462. package/dist/ui/components/Select.style.css +7 -21
  463. package/dist/ui/components/Semantic.js +27 -3
  464. package/dist/ui/components/TagInput.style.css +4 -9
  465. package/dist/ui/components/Textarea.style.css +4 -18
  466. package/dist/ui/components/Toggle.style.css +1 -1
  467. package/dist/ui/components/Typography.d.ts +3 -3
  468. package/dist/ui/components/Typography.js +15 -15
  469. package/dist/ui/components/ViewToggle.style.css +4 -4
  470. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  471. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  472. package/dist/ui/forms/ColorPickerField.js +15 -2
  473. package/dist/ui/forms/FieldCheckbox.js +15 -2
  474. package/dist/ui/forms/FieldInput.d.ts +22 -1
  475. package/dist/ui/forms/FieldInput.js +19 -5
  476. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  477. package/dist/ui/forms/FieldLabel.js +70 -0
  478. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  479. package/dist/ui/forms/FieldSelect.js +27 -5
  480. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  481. package/dist/ui/forms/FieldTextarea.js +22 -5
  482. package/dist/ui/forms/FormShell.d.ts +17 -1
  483. package/dist/ui/forms/FormShell.js +36 -2
  484. package/dist/ui/forms/FormShell.style.css +41 -33
  485. package/dist/ui/forms/index.d.ts +5 -1
  486. package/dist/ui/forms/index.js +2 -0
  487. package/dist/ui/icons/icon-registry.d.ts +1 -0
  488. package/dist/ui/icons/icon-registry.js +17 -1
  489. package/dist/utils/id-generators.d.ts +80 -0
  490. package/dist/utils/id-generators.js +93 -0
  491. package/dist/utils/search-txt-builders.d.ts +12 -0
  492. package/dist/utils/search-txt-builders.js +17 -0
  493. package/package.json +1 -1
  494. package/scripts/seed-cli.mjs +106 -5
  495. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  497. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  498. package/dist/features/loyalty/api/balance/route.js +0 -41
  499. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  500. package/dist/features/loyalty/api/earn/route.js +0 -38
  501. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  502. package/dist/features/loyalty/api/history/route.js +0 -58
  503. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  504. package/dist/features/loyalty/api/redeem/route.js +0 -38
  505. package/dist/features/loyalty/columns/index.d.ts +0 -17
  506. package/dist/features/loyalty/columns/index.js +0 -40
  507. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  508. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  509. package/dist/features/loyalty/components/index.d.ts +0 -1
  510. package/dist/features/loyalty/components/index.js +0 -1
  511. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  513. package/dist/features/loyalty/index.d.ts +0 -6
  514. package/dist/features/loyalty/index.js +0 -6
  515. package/dist/features/loyalty/manifest.d.ts +0 -2
  516. package/dist/features/loyalty/manifest.js +0 -16
  517. package/dist/features/loyalty/messages/en.json +0 -19
  518. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  519. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  520. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  521. package/dist/features/loyalty/schemas/index.js +0 -40
  522. package/dist/features/loyalty/server.d.ts +0 -6
  523. package/dist/features/loyalty/server.js +0 -6
  524. package/dist/features/loyalty/types/index.d.ts +0 -31
  525. package/dist/features/loyalty/types/index.js +0 -1
  526. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  527. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  528. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  529. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  530. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  531. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { useRouter } from "next/navigation.js";
5
5
  import { z } from "zod";
6
6
  import { FormShell, SectionForm, useSectionFormNav, useFormShell } from "../../shell/index.js";
7
- import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms/index.js";
7
+ import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary, AddAnotherToggle, addAnotherSubmitLabel, } from "../../../ui/forms/index.js";
8
8
  import { Alert, Button, Div, FormField, FormGroup, Heading, Section, Stack, TagInput, Text, Toggle, useToast, Row } from "../../../ui/index.js";
9
9
  import { ImageUpload, MediaUploadField, MediaUploadList, useMediaUpload } from "../../media/index.js";
10
10
  import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddressSelectorCreate.js";
@@ -15,6 +15,9 @@ import { pluginFor } from "../../../_internal/shared/listing-types/_registry.js"
15
15
  import { DEFAULT_MIN_OFFER_PERCENT } from "../../../_internal/shared/features/offers/config.js";
16
16
  import { PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "../../../_internal/shared/media/limits.js";
17
17
  import { isFinalSale } from "../../products/constants/final-sale.js";
18
+ // The one `ProductImage[] | string[]` → `string[]` normaliser — a value
19
+ // import, needed at runtime by `galleryFields` below.
20
+ import { productImageUrls } from "../../products/schemas/firestore.js";
18
21
  import { ProductFeaturesSelector } from "../../products/components/ProductFeaturesSelector.js";
19
22
  import { normalizeError } from "../../../errors/normalize.js";
20
23
  /**
@@ -143,7 +146,20 @@ function StepBasic({ values, onChange, renderCategorySelector, renderBrandSelect
143
146
  : /* And back again drops the count, for the same reason. */
144
147
  { isLot: false, lotSize: undefined }), label: values.isLot ? "A lot of several items" : "A single item", size: "sm" }), values.isLot && (_jsx(FormField, { name: "lotSize", label: "How many?", type: "number", value: values.lotSize != null ? String(values.lotSize) : "", onChange: (v) => onChange({ lotSize: v ? Number(v) : undefined }), placeholder: "optional" }))] }), _jsx(Text, { size: "xs", color: "muted", children: values.isLot
145
148
  ? "A lot files under a category rather than one model, because a category is a collection — naming one model would claim every item in the pile is that model."
146
- : "A single item can be filed against the exact bey or part it is, so buyers searching that name find it." })] }), _jsxs(FormGroup, { columns: 2, children: [renderCategorySelector ? (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] mb-1", size: "sm", weight: "medium", children: "Category" }), renderCategorySelector({
149
+ : "A single item can be filed against the exact bey or part it is, so buyers searching that name find it." })] }), _jsxs(FormGroup, { columns: 2, children: [renderCategorySelector ? (
150
+ /*
151
+ * 🛑 ONE element, not a Fragment. `.appkit-form-group` is
152
+ * `display: grid` (Form.style.css), and a Fragment renders no DOM
153
+ * node — so `<>{label}{picker}</>` handed the grid THREE children
154
+ * instead of two. At `md:grid-cols-2` that put the word "Category"
155
+ * alone in the left cell, the picker in the right cell, and pushed
156
+ * Condition onto its own row: three different label layouts in one
157
+ * section, from one pair of angle brackets.
158
+ *
159
+ * `FormField` (the else branch, and Condition beside it) renders its
160
+ * own single wrapper, which is why only this branch was affected.
161
+ */
162
+ _jsxs(Div, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] mb-1", size: "sm", weight: "medium", children: "Category" }), renderCategorySelector({
147
163
  value: values.category ?? "",
148
164
  onChange: (v) => onChange({ category: v }),
149
165
  isLot: values.isLot === true,
@@ -170,7 +186,7 @@ function StepBasic({ values, onChange, renderCategorySelector, renderBrandSelect
170
186
  context: templateContext,
171
187
  isDescriptionDirty,
172
188
  onApply: onChange,
173
- }), _jsx(FormField, { name: "description", label: "Description", type: "textarea", value: description, onChange: (v) => onChange({ description: v }), placeholder: "Describe your listing in detail\u2026" }), renderBrandSelector ? (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] mb-1", size: "sm", weight: "medium", children: "Brand" }), renderBrandSelector({ value: values.brand ?? "", onChange: (v) => onChange({ brand: v }) })] })) : (_jsx(FormField, { name: "brand", label: "Brand", type: "text", value: values.brand ?? "", onChange: (v) => onChange({ brand: v }), placeholder: "e.g. Pok\u00E9mon Company" })), _jsx(TagInput, { label: "Tags", value: values.tags ?? [], onChange: (tags) => onChange({ tags }), placeholder: "pokemon, psa9, charizard, holo" }), _jsx(BarcodeField, { value: values.barcodeId ?? "", onChange: (v) => onChange({ barcodeId: v || undefined }), onScan: (v) => onChange({ barcodeId: v }), helperText: "Leave blank to auto-generate. Scan a pre-printed sticker to link it." })] }));
189
+ }), _jsx(FormField, { name: "description", label: "Description", type: "textarea", value: description, onChange: (v) => onChange({ description: v }), placeholder: "Describe your listing in detail\u2026" }), renderBrandSelector ? (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] mb-1", size: "sm", weight: "medium", children: "Brand" }), renderBrandSelector({ value: values.brand ?? "", onChange: (v) => onChange({ brand: v }) })] })) : (_jsx(FormField, { name: "brand", label: "Brand", type: "text", value: values.brand ?? "", onChange: (v) => onChange({ brand: v }), placeholder: "e.g. Pok\u00E9mon Company" })), _jsx(TagInput, { label: "Tags", value: values.tags ?? [], onChange: (tags) => onChange({ tags }), placeholder: "pokemon, psa9, charizard, holo" }), _jsx(FormField, { name: "sku", label: "SKU", type: "text", value: values.sku ?? "", onChange: (v) => onChange({ sku: v || undefined }), placeholder: "LIR-BURS-001", hint: "Your own stock code. Leave blank and one is generated from the category." }), _jsx(BarcodeField, { value: values.barcodeId ?? "", onChange: (v) => onChange({ barcodeId: v || undefined }), onScan: (v) => onChange({ barcodeId: v }), helperText: "Leave blank to auto-generate. Scan a pre-printed sticker to link it." })] }));
174
190
  }
175
191
  // ── Step 1b: Details — the inherited-but-editable fields (C2c) ───────────────
176
192
  const AUTHENTICITY_OPTIONS = [
@@ -211,13 +227,21 @@ function StepMedia({ values, onChange, storeSlug = "store", }) {
211
227
  /*
212
228
  * One control for both media kinds.
213
229
  *
214
- * The document keeps `images: string[]` and a single `video` — unchanged —
215
- * so this seeds from both and splits again on write. Collapsing the two
216
- * into one stored `media[]` would be a Firestore migration touching cards,
217
- * detail, cart, orders and search; the input surface is what needed fixing.
230
+ * This seeds from `images` + `video` and splits them again on write.
231
+ * Collapsing the two into one stored `media[]` would be a Firestore
232
+ * migration touching cards, detail, cart, orders and search; the input
233
+ * surface is what needed fixing.
234
+ *
235
+ * 🛑 `images` IS NOT `string[]` AT RUNTIME. This comment asserted it was,
236
+ * and the line below mapped each entry as a URL — but
237
+ * `ProductDocument.images` has been `ProductImage[]` (objects) since A6, so
238
+ * the seller's own gallery tiles rendered `MediaImage`'s 📦 placeholder
239
+ * instead of their photos. Same defect as `ProductForm`, same cause: a cast
240
+ * at the seeding boundary claims `Partial<ProductItem>` while the document
241
+ * holds objects. `productImageUrls` accepts both shapes.
218
242
  */
219
243
  const galleryFields = [
220
- ...(values.images ?? []).map((url) => ({ url, type: "image" })),
244
+ ...productImageUrls(values.images).map((url) => ({ url, type: "image" })),
221
245
  ...(values.video?.url
222
246
  ? [{
223
247
  url: values.video.url,
@@ -508,7 +532,18 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
508
532
  * yet — it needs the sections — so it is composed in below. That ordering is
509
533
  * why the original `wizardShellCtx` existed; only its contents change.
510
534
  */
511
- const { shellCtx: baseShellCtx, setFieldError, clearErrors, validate } = useFormShellState(sellerProductSchema);
535
+ const { shellCtx: baseShellCtx, setFieldError, clearErrors, validate, markSubmitAttempted } =
536
+ /*
537
+ * 🛑 "Create & add another" OFFERED ONLY IN CREATE MODE. Editing an
538
+ * existing listing and then "adding another" would have to mean "save this
539
+ * one, then start a blank one" — a different action, and not one anybody
540
+ * reaches for while fixing a typo. The toggle hides itself on edit too,
541
+ * but gating the OFFER means the branch below cannot be reached from edit
542
+ * at all rather than relying on a component to hide.
543
+ */
544
+ useFormShellState(sellerProductSchema, undefined, {
545
+ allowAddAnother: mode === "create",
546
+ });
512
547
  const [stepError, setStepError] = useState(null);
513
548
  const { isDirty, markDirty, markClean } = useFormShell();
514
549
  const router = useRouter();
@@ -546,6 +581,33 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
546
581
  else
547
582
  router.back();
548
583
  }, [onDiscard, router]);
584
+ /*
585
+ * ── "Create & add another": what survives, and what must not ──────────────
586
+ *
587
+ * 🛑 AN ALLOW-LIST, NOT A DELETE-LIST. The obvious implementation is
588
+ * `{ ...draft, title: "", images: [] }` — spread the last one and blank the
589
+ * bits that look item-specific. That is the deny-list shape this codebase
590
+ * has been bitten by all session (Root Cause #70: a public payload built by
591
+ * spreading a document and deleting three keys published everything nobody
592
+ * thought to delete). Here it would carry the previous listing's barcode,
593
+ * its SKU, its slug, its SEO title, its stock count and its video into the
594
+ * next record — and `barcodeId` and `slug` are UNIQUE, so the second save
595
+ * would either collide or silently overwrite the first listing.
596
+ *
597
+ * So the next draft is built from NOTHING plus the four fields a batch
598
+ * genuinely shares. A field added to the draft type later is excluded by
599
+ * default, which is the direction that cannot lose data.
600
+ *
601
+ * `listingType` and the store are PROPS, not draft fields, so they persist
602
+ * without being listed — they are part of which editor is mounted.
603
+ */
604
+ const nextDraftForAnother = useCallback((saved) => ({
605
+ status: "draft",
606
+ condition: saved.condition ?? "new",
607
+ category: saved.category,
608
+ brand: saved.brand,
609
+ }), []);
610
+ const addAnother = baseShellCtx.addAnother;
549
611
  // `silent` suppresses the success toast — used by the debounced auto-save
550
612
  // effect above so it doesn't pop a "Saved." toast every couple of seconds
551
613
  // while the seller is still typing. The manual "Save Draft"/"Save Changes"
@@ -567,6 +629,30 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
567
629
  return;
568
630
  }
569
631
  applyZodIssues(result.issues ?? [], setFieldError);
632
+ /*
633
+ * 🛑 WITHOUT THIS THE TOAST IS A LIE — Root Cause #74, regressed here.
634
+ *
635
+ * `<FormErrorSummary>` early-returns `null` unless `ctx.submitAttempted`,
636
+ * and the per-field inline errors gate on `touched` — so on a field the
637
+ * seller never visited, nothing renders either. This form's Save and
638
+ * Publish are `type="button"` with `onClick`, which fires no native
639
+ * submit event, so nothing was marking the attempt. The result was a
640
+ * toast reading "Fix the highlighted errors and try again" with
641
+ * **nothing highlighted anywhere on the page** — four of them stacked up
642
+ * in the reported screenshot, and no way to find a single field.
643
+ *
644
+ * CLAUDE.md states the rule exactly: "Marking the attempt has to happen
645
+ * where an attempt happens", and a `type="button"` submit must call this
646
+ * itself. It is marked only on the FAILURE path: a save that succeeds
647
+ * has no errors to reveal, and flipping the flag anyway would arm the
648
+ * summary for every later keystroke.
649
+ *
650
+ * Marked even when `silent` (auto-save). The summary is the only durable
651
+ * record of what is wrong — auto-save deliberately suppresses its toast
652
+ * to avoid one every two seconds, so suppressing the summary too would
653
+ * leave the seller with no signal at all.
654
+ */
655
+ markSubmitAttempted();
570
656
  if (!silent) {
571
657
  showToast(saveFailureMessage(result.code, (result.issues ?? []).length), "error");
572
658
  }
@@ -576,7 +662,7 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
576
662
  if (!silent)
577
663
  showToast("Failed to save.", "error");
578
664
  }
579
- }, [draft, onSave, markClean, clearErrors, setFieldError, showToast]);
665
+ }, [draft, onSave, markClean, clearErrors, setFieldError, showToast, markSubmitAttempted]);
580
666
  const handleAutoSave = useCallback(async () => {
581
667
  try {
582
668
  await handleSave(true);
@@ -605,17 +691,41 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
605
691
  if (result.ok) {
606
692
  markClean();
607
693
  clearErrors();
694
+ /*
695
+ * 🛑 THE TOAST NAMES WHAT WAS PUBLISHED, because the form is about to
696
+ * be empty. "Published." over a blank form reads as though the thing
697
+ * on screen vanished; "Published Dran Sword. Next one — category and
698
+ * condition kept." says what happened and what was kept, which is the
699
+ * only confirmation the seller gets that their work landed.
700
+ */
701
+ if (addAnother) {
702
+ const title = draft.title?.trim();
703
+ setDraft(nextDraftForAnother(draft));
704
+ /*
705
+ * `markClean()` ran above, and the fresh draft must stay clean:
706
+ * `update()` marks dirty, and `setDraft` here deliberately bypasses
707
+ * it. Otherwise the 2-second create-mode auto-save effect fires on
708
+ * the empty draft and writes a second, blank listing.
709
+ */
710
+ showToast(title
711
+ ? `Published ${title}. Next one — category and condition kept.`
712
+ : "Published. Next one — category and condition kept.", "success");
713
+ return;
714
+ }
608
715
  showToast("Published.", "success");
609
716
  return;
610
717
  }
611
718
  applyZodIssues(result.issues ?? [], setFieldError);
719
+ // See the note on the same call in `handleSave` — without this the
720
+ // "Fix the highlighted errors" toast points at nothing (Root Cause #74).
721
+ markSubmitAttempted();
612
722
  showToast(saveFailureMessage(result.code, (result.issues ?? []).length), "error");
613
723
  }
614
724
  catch (err) {
615
725
  void normalizeError(err);
616
726
  showToast("Failed to publish.", "error");
617
727
  }
618
- }, [draft, onPublish, markClean, clearErrors, setFieldError, showToast]);
728
+ }, [draft, onPublish, markClean, clearErrors, setFieldError, showToast, addAnother, nextDraftForAnother, markSubmitAttempted]);
619
729
  const listingTypeLabel = pluginForMode(listingType).typeLabel;
620
730
  const typeSpecificSection = TYPE_SPECIFIC_SECTIONS[listingType] ?? null;
621
731
  const sections = [
@@ -749,7 +859,7 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
749
859
  }) }) }) }));
750
860
  }
751
861
  if (mode === "create") {
752
- return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, splitPreview: !!previewSlot, renderBottomBar: () => (_jsxs(Div, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(Div, { paddingX: "x-5", padding: "y-sm", children: _jsx(FormErrorSummary, {}) }) }), _jsx(Div, { paddingX: "x-5", padding: "b-sm", children: _jsxs(Row, { gap: "sm", justify: "between", wrap: true, children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => void handleSave(), disabled: isLoading || !draft.title?.trim(), children: "Save as draft & finish later" }), _jsx(Button, { variant: "primary", onClick: () => void handlePublish(), isLoading: isLoading, disabled: isLoading, children: `Publish ${listingTypeLabel}` })] }) }), stepError && (_jsx(Text, { className: "text-[var(--appkit-color-error)] px-[1.25rem] pb-[0.75rem]", size: "sm", children: stepError }))] })), children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(SectionForm, { sections: sections, values: draft, onChange: update, onSubmit: handlePublish, schema: sellerProductSchema, openIds: openIds, onOpenChange: setOpenIds, isLoading: isLoading, hideActions: true }) }) }));
862
+ return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, splitPreview: !!previewSlot, renderBottomBar: () => (_jsxs(Div, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(Div, { paddingX: "x-5", padding: "y-sm", children: _jsx(FormErrorSummary, {}) }) }), _jsxs(Div, { paddingX: "x-5", padding: "b-sm", children: [_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(AddAnotherToggle, { noun: listingTypeLabel.toLowerCase(), isCreate: mode === "create" }) }), _jsxs(Row, { gap: "sm", justify: "between", wrap: true, children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => void handleSave(), disabled: isLoading || !draft.title?.trim(), children: "Save as draft & finish later" }), _jsx(Button, { variant: "primary", onClick: () => void handlePublish(), isLoading: isLoading, disabled: isLoading, children: addAnotherSubmitLabel(`Publish ${listingTypeLabel}`, addAnother) })] })] }), stepError && (_jsx(Text, { className: "text-[var(--appkit-color-error)] px-[1.25rem] pb-[0.75rem]", size: "sm", children: stepError }))] })), children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(SectionForm, { sections: sections, values: draft, onChange: update, onSubmit: handlePublish, schema: sellerProductSchema, openIds: openIds, onOpenChange: setOpenIds, isLoading: isLoading, hideActions: true }) }) }));
753
863
  }
754
864
  // Edit mode — FormShell with section nav + full form
755
865
  const editSections = [
@@ -759,5 +869,5 @@ productId, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCat
759
869
  return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, sections: editSections, onSaveDraft: handleSave, onPublish: handlePublish, saveLabel: "Save Changes", publishLabel: "Update", previewSlot: previewSlot, footerTopSlot: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(FormErrorSummary, {}) }), children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Section, { id: "basic", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Basic Info" }), _jsx(StepBasic, { values: draft, onChange: update, renderCategorySelector: renderCategorySelector, renderBrandSelector: renderBrandSelector, renderTemplateSelector: renderTemplateSelector })] }), _jsxs(Section, { id: "media", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Media" }), _jsx(StepMedia, { values: draft, onChange: update, storeSlug: storeSlug })] }), typeSpecificSection && (_jsxs(Section, { id: typeSpecificSection.id, children: [_jsx(Heading, { level: 3, className: "mb-4", children: typeSpecificSection.sectionHeading ?? typeSpecificSection.label }), typeSpecificSection.render({ values: draft, onChange: update, errors: {} }), mode === "edit" &&
760
870
  listingType === "digital-code" &&
761
871
  productId &&
762
- renderDigitalContentPool?.(productId)] })), _jsxs(Section, { id: "pricing", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pricing" }), _jsx(StepPricing, { values: draft, onChange: update, listingType: listingType })] }), _jsxs(Section, { id: "shipping", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Shipping" }), _jsx(StepShipping, { values: draft, onChange: update, renderAddressSelector: renderAddressSelector })] }), _jsxs(Section, { id: "returns", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Returns" }), _jsx(StepReturns, { values: draft, onChange: update })] }), _jsxs(Section, { id: "publish", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Publish" }), _jsx(StepPublish, { values: draft, onChange: update }), onSaveAsTemplate && (_jsxs(Div, { className: "mt-4 border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onSaveAsTemplate(draft), children: "Save as Template" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-secondary-text)]", size: "xs", children: "Save these settings as a reusable template for future listings." })] }))] })] }) }) }));
872
+ renderDigitalContentPool?.(productId)] })), _jsxs(Section, { id: "pricing", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pricing" }), _jsx(StepPricing, { values: draft, onChange: update, listingType: listingType })] }), _jsxs(Section, { id: "shipping", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Shipping" }), _jsx(StepShipping, { values: draft, onChange: update, renderAddressSelector: renderAddressSelector })] }), _jsxs(Section, { id: "returns", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Returns" }), _jsx(StepReturns, { values: draft, onChange: update })] }), _jsxs(Section, { id: "publish", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Publish" }), _jsx(StepPublish, { values: draft, onChange: update }), onSaveAsTemplate && (_jsxs(Div, { className: "mt-4 border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onSaveAsTemplate(draft), children: "Save as Template" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)]", size: "xs", children: "Save these settings as a reusable template for future listings." })] }))] })] }) }) }));
763
873
  }
@@ -188,6 +188,8 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
188
188
  const config = {
189
189
  portal: "seller",
190
190
  title: "Reviews",
191
+ // The page authors its own visible <h1>, so the shell must not add a second.
192
+ hideHeader: true,
191
193
  searchPlaceholder: "Search product, reviewer, or comment…",
192
194
  emptyLabel: "No reviews found.",
193
195
  filterKeys: ["rating", "replied"],
@@ -226,7 +228,7 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
226
228
  setBulkReplyOpen(true);
227
229
  },
228
230
  })),
229
- renderCards: (rows, _view, selection, isLoading) => (_jsx(Stack, { gap: "md", children: isLoading ? (_jsx(Div, { className: "text-center", padding: "y-xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Loading reviews\u2026" }) })) : rows.length === 0 ? (_jsx(Div, { className: "text-center", padding: "y-3xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "No reviews found." }) })) : (rows.map((review) => (_jsx(Div, { className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "lg", padding: "md", children: _jsxs(Row, { align: "start", justify: "between", gap: "3", wrap: true, children: [_jsxs(Row, { align: "start", className: "flex-1 min-w-0", gap: "3", children: [_jsx(Checkbox, { checked: selection.selectedIds.includes(review.id), onChange: () => selection.toggleSelect(review.id), "aria-label": "Select review" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", weight: "medium", children: review.productTitle }), _jsxs(Row, { className: "mt-1", align: "center", gap: "sm", wrap: true, children: [_jsx(Stars, { rating: review.rating }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["by ", review.userName] }), review.verified && _jsx(Badge, { variant: "success", children: "Verified" }), statusBadge(review.status), _jsx(Badge, { variant: review.sellerReply ? "success" : "warning", children: review.sellerReply ? "Store replied" : "Awaiting store reply" })] }), review.title && _jsx(Text, { className: "mt-2", weight: "medium", children: review.title }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-secondary)] line-clamp-3", size: "sm", children: review.comment }), review.sellerReply && (_jsxs(Div, { className: "mt-2 pl-[0.75rem] border-l-2 border-[var(--appkit-color-primary)]", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Store reply:" }), _jsx(Text, { size: "sm", children: review.sellerReply })] }))] })] }), _jsxs(Row, { className: "flex-shrink-0", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => openReply(review), children: review.sellerReply ? "Edit Reply" : ACTIONS.STORE["reply-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setContestTarget(review), children: ACTIONS.STORE["contest-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFeedbackTarget(review), children: ACTIONS.STORE["buyer-feedback"].label })] })] }) }, review.id)))) })),
231
+ renderCards: (rows, _view, selection, isLoading) => (_jsx(Stack, { gap: "md", children: isLoading ? (_jsx(Div, { className: "text-center", padding: "y-xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Loading reviews\u2026" }) })) : rows.length === 0 ? (_jsx(Div, { className: "text-center", padding: "y-3xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "No reviews found." }) })) : (rows.map((review) => (_jsx(Div, { className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "lg", padding: "md", children: _jsxs(Row, { align: "start", justify: "between", gap: "3", wrap: true, children: [_jsxs(Row, { align: "start", className: "flex-1 min-w-0", gap: "3", children: [_jsx(Checkbox, { checked: selection.selectedIds.includes(review.id), onChange: () => selection.toggleSelect(review.id), "aria-label": "Select review" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", weight: "medium", children: review.productTitle }), _jsxs(Row, { className: "mt-1", align: "center", gap: "sm", wrap: true, children: [_jsx(Stars, { rating: review.rating }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["by ", review.userName] }), review.verified && _jsx(Badge, { variant: "success", children: "Verified" }), statusBadge(review.status), _jsx(Badge, { variant: review.sellerReply ? "success" : "warning", children: review.sellerReply ? "Store replied" : "Awaiting store reply" })] }), review.title && _jsx(Text, { className: "mt-2", weight: "medium", children: review.title }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)] line-clamp-3", size: "sm", children: review.comment }), review.sellerReply && (_jsxs(Div, { className: "mt-2 pl-[0.75rem] border-l-2 border-[var(--appkit-color-primary)]", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Store reply:" }), _jsx(Text, { size: "sm", children: review.sellerReply })] }))] })] }), _jsxs(Row, { className: "flex-shrink-0", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => openReply(review), children: review.sellerReply ? "Edit Reply" : ACTIONS.STORE["reply-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setContestTarget(review), children: ACTIONS.STORE["contest-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFeedbackTarget(review), children: ACTIONS.STORE["buyer-feedback"].label })] })] }) }, review.id)))) })),
230
232
  };
231
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(SideDrawer, { isOpen: !!replyTarget, onClose: () => setReplyTarget(null), title: replyTarget?.sellerReply ? "Edit Reply" : "Reply to Review", mode: "create", children: _jsxs(Stack, { gap: "md", className: `${__P.p4}`, children: [replyTarget && (_jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-surface-muted)]`, rounded: "default", children: [_jsx(Stars, { rating: replyTarget.rating }), _jsx(Text, { className: "mt-1", size: "sm", children: replyTarget.comment })] })), replyError && _jsx(Alert, { variant: "error", children: replyError }), _jsx(ReviewTextForm, { schema: reviewReplySchema, values: replyDraft, onChange: (partial) => setReplyDraft((d) => ({ ...d, ...partial })), onSubmit: () => void handleReplySave(refetchReviews), submitLabel: replyTarget?.sellerReply ? "Update Reply" : "Post Reply", onCancel: () => setReplyTarget(null), isLoading: replySaving, scope: "store:review-reply" })] }) }), _jsx(Modal, { isOpen: bulkReplyOpen, onClose: () => setBulkReplyOpen(false), title: "Bulk reply to selected reviews", children: _jsx(Stack, { gap: "md", padding: "2xs", children: _jsx(ReviewTextForm, { schema: reviewBulkReplySchema, values: bulkDraft, onChange: (partial) => setBulkDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitBulkReply(), submitLabel: "Send reply", onCancel: () => setBulkReplyOpen(false), isLoading: bulkSaving, scope: "store:review-bulk-reply" }) }) }), _jsx(Modal, { isOpen: !!contestTarget, onClose: () => setContestTarget(null), title: "Contest this review", children: _jsxs(Stack, { gap: "md", padding: "2xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Flag this review for admin investigation." }), _jsx(ReviewTextForm, { schema: reviewContestSchema, values: contestDraft, onChange: (partial) => setContestDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitContest(refetchReviews), submitLabel: "Submit", onCancel: () => setContestTarget(null), scope: "store:review-contest" })] }) }), _jsx(Modal, { isOpen: !!feedbackTarget, onClose: () => setFeedbackTarget(null), title: "Send feedback to buyer", children: _jsx(Stack, { gap: "md", padding: "2xs", children: _jsx(ReviewTextForm, { schema: reviewFeedbackSchema, values: feedbackDraft, onChange: (partial) => setFeedbackDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitFeedback(), submitLabel: "Send", onCancel: () => setFeedbackTarget(null), scope: "store:review-feedback" }) }) })] }));
233
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(SideDrawer, { isOpen: !!replyTarget, onClose: () => setReplyTarget(null), title: replyTarget?.sellerReply ? "Edit Reply" : "Reply to Review", mode: "create", children: _jsxs(Stack, { gap: "md", className: `${__P.p4}`, children: [replyTarget && (_jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-bg)]`, rounded: "default", children: [_jsx(Stars, { rating: replyTarget.rating }), _jsx(Text, { className: "mt-1", size: "sm", children: replyTarget.comment })] })), replyError && _jsx(Alert, { variant: "error", children: replyError }), _jsx(ReviewTextForm, { schema: reviewReplySchema, values: replyDraft, onChange: (partial) => setReplyDraft((d) => ({ ...d, ...partial })), onSubmit: () => void handleReplySave(refetchReviews), submitLabel: replyTarget?.sellerReply ? "Update Reply" : "Post Reply", onCancel: () => setReplyTarget(null), isLoading: replySaving, scope: "store:review-reply" })] }) }), _jsx(Modal, { isOpen: bulkReplyOpen, onClose: () => setBulkReplyOpen(false), title: "Bulk reply to selected reviews", children: _jsx(Stack, { gap: "md", padding: "2xs", children: _jsx(ReviewTextForm, { schema: reviewBulkReplySchema, values: bulkDraft, onChange: (partial) => setBulkDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitBulkReply(), submitLabel: "Send reply", onCancel: () => setBulkReplyOpen(false), isLoading: bulkSaving, scope: "store:review-bulk-reply" }) }) }), _jsx(Modal, { isOpen: !!contestTarget, onClose: () => setContestTarget(null), title: "Contest this review", children: _jsxs(Stack, { gap: "md", padding: "2xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Flag this review for admin investigation." }), _jsx(ReviewTextForm, { schema: reviewContestSchema, values: contestDraft, onChange: (partial) => setContestDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitContest(refetchReviews), submitLabel: "Submit", onCancel: () => setContestTarget(null), scope: "store:review-contest" })] }) }), _jsx(Modal, { isOpen: !!feedbackTarget, onClose: () => setFeedbackTarget(null), title: "Send feedback to buyer", children: _jsx(Stack, { gap: "md", padding: "2xs", children: _jsx(ReviewTextForm, { schema: reviewFeedbackSchema, values: feedbackDraft, onChange: (partial) => setFeedbackDraft((d) => ({ ...d, ...partial })), onSubmit: () => void submitFeedback(), submitLabel: "Send", onCancel: () => setFeedbackTarget(null), scope: "store:review-feedback" }) }) })] }));
232
234
  }
@@ -84,7 +84,7 @@ export function StoreSidebar({ items, groups, activeHref, storeName, storeLogoUR
84
84
  const navContent = groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close }));
85
85
  if (variant === "sidebar") {
86
86
  const handleToggle = onToggle ?? close;
87
- return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { surface: "overlay-xs", className: "hidden lg:block fixed inset-0 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: `hidden lg:flex fixed ${rail.edgeClass} z-40 transition-transform duration-300 top-[var(--header-height,3.5rem)] h-[calc(100vh-var(--header-height,3.5rem))] w-[18rem] ${desktopOpen ? "translate-x-0" : rail.collapsedTranslateClass}`, children: [_jsxs(Stack, { border: "default", surface: "sidePanel", className: `flex-1 ${rail.borderClass} ${__O.hidden}`, shadow: "xl", children: [_jsx(Div, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", children: _jsxs(Row, { className: "min-w-0", align: "center", gap: "3", children: [storeLogoURL ? (_jsx(Div, { className: CLS_STORE_AVATAR, children: _jsx(MediaImage, { src: storeLogoURL, alt: storeName ?? "", size: "thumbnail" }) })) : (_jsx(Div, { className: CLS_STORE_FALLBACK, children: storeName?.[0]?.toUpperCase() })), _jsx(Text, { className: CLS_STORE_NAME, children: storeName || panelTitle })] }) }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: navContent }), renderFooter && _jsx(Div, { border: "top", padding: "inline", children: renderFooter() })] }), _jsx(SidebarCollapseToggle, { expanded: desktopOpen, onToggle: handleToggle, edge: hand })] }), _jsx(Div, { className: "lg:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
87
+ return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: `hidden lg:flex fixed ${rail.edgeClass} z-40 transition-transform duration-300 top-[var(--header-height,3.5rem)] h-[calc(100vh-var(--header-height,3.5rem))] w-[18rem] ${desktopOpen ? "translate-x-0" : rail.collapsedTranslateClass}`, children: [_jsxs(Stack, { border: "default", surface: "sidePanel", className: `flex-1 ${rail.borderClass} ${__O.hidden}`, shadow: "xl", children: [_jsx(Div, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", children: _jsxs(Row, { className: "min-w-0", align: "center", gap: "3", children: [storeLogoURL ? (_jsx(Div, { className: CLS_STORE_AVATAR, children: _jsx(MediaImage, { src: storeLogoURL, alt: storeName ?? "", size: "thumbnail" }) })) : (_jsx(Div, { className: CLS_STORE_FALLBACK, children: storeName?.[0]?.toUpperCase() })), _jsx(Text, { className: CLS_STORE_NAME, children: storeName || panelTitle })] }) }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: navContent }), renderFooter && _jsx(Div, { border: "top", padding: "inline", children: renderFooter() })] }), _jsx(SidebarCollapseToggle, { expanded: desktopOpen, onToggle: handleToggle, edge: hand })] }), _jsx(Div, { className: "lg:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
88
88
  }
89
89
  return (_jsxs(_Fragment, { children: [mounted && mobileOpen &&
90
90
  createPortal(_jsx(DrawerPanel, { title: panelTitle, onClose: close, children: navContent }), document.body), _jsx(Div, { className: "lg:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
@@ -200,6 +200,16 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
200
200
  buyQuantity: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
201
201
  getQuantity: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
202
202
  currentUsage: z.ZodOptional<z.ZodNumber>;
203
+ tiers: z.ZodOptional<z.ZodArray<z.ZodObject<{
204
+ minAmount: z.ZodNumber;
205
+ discountValue: z.ZodNumber;
206
+ }, "strip", z.ZodTypeAny, {
207
+ minAmount: number;
208
+ discountValue: number;
209
+ }, {
210
+ minAmount: number;
211
+ discountValue: number;
212
+ }>, "many">>;
203
213
  firstTimeUserOnly: z.ZodBoolean;
204
214
  appliesToAuctions: z.ZodBoolean;
205
215
  }, "strip", z.ZodTypeAny, {
@@ -218,6 +228,10 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
218
228
  appliesToAuctions: boolean;
219
229
  value?: string | undefined;
220
230
  description?: string | undefined;
231
+ tiers?: {
232
+ minAmount: number;
233
+ discountValue: number;
234
+ }[] | undefined;
221
235
  currentUsage?: number | undefined;
222
236
  maxDiscount?: string | undefined;
223
237
  buyQuantity?: string | undefined;
@@ -238,6 +252,10 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
238
252
  appliesToAuctions: boolean;
239
253
  value?: string | undefined;
240
254
  description?: string | undefined;
255
+ tiers?: {
256
+ minAmount: number;
257
+ discountValue: number;
258
+ }[] | undefined;
241
259
  currentUsage?: number | undefined;
242
260
  maxDiscount?: string | undefined;
243
261
  buyQuantity?: string | undefined;
@@ -258,6 +276,10 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
258
276
  appliesToAuctions: boolean;
259
277
  value?: string | undefined;
260
278
  description?: string | undefined;
279
+ tiers?: {
280
+ minAmount: number;
281
+ discountValue: number;
282
+ }[] | undefined;
261
283
  currentUsage?: number | undefined;
262
284
  maxDiscount?: string | undefined;
263
285
  buyQuantity?: string | undefined;
@@ -278,6 +300,10 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
278
300
  appliesToAuctions: boolean;
279
301
  value?: string | undefined;
280
302
  description?: string | undefined;
303
+ tiers?: {
304
+ minAmount: number;
305
+ discountValue: number;
306
+ }[] | undefined;
281
307
  currentUsage?: number | undefined;
282
308
  maxDiscount?: string | undefined;
283
309
  buyQuantity?: string | undefined;
@@ -246,6 +246,33 @@ export const adminCouponFormSchema = couponFormBase
246
246
  section: "limits", order: 5, row: "pair", derived: true, tier: "t3-mirror",
247
247
  label: "Current usage", help: "Updated by orders.",
248
248
  }),
249
+ /*
250
+ * ── Tiered discounts ────────────────────────────────────────────────────
251
+ *
252
+ * 🛑 `CouponDocument.tiers` has existed since the collection shipped and
253
+ * had NO editor on either portal — and `getTieredDiscount`, its helper,
254
+ * had zero callers, so even a hand-seeded tier was ignored by the
255
+ * discount calculation. Both halves are fixed together: wiring an editor
256
+ * to a field nothing reads would hand an admin a control that does
257
+ * nothing, which is the defect this audit keeps removing rather than
258
+ * adding.
259
+ *
260
+ * 🛑 `when` limits it to the two types that HAVE a `discount.value` for a
261
+ * tier to replace. A BOGO's value lives in `bxgy` and free shipping is a
262
+ * shipping-line waiver, so a tier on either would be meaningless — and an
263
+ * input that cannot affect the outcome is worse than an absent one.
264
+ */
265
+ tiers: annotate(z
266
+ .array(z.object({
267
+ minAmount: z.number().min(0, "Spend threshold cannot be negative"),
268
+ discountValue: z.number().min(0, "Discount cannot be negative"),
269
+ }))
270
+ .max(6, "At most 6 tiers")
271
+ .optional(), {
272
+ section: "limits", order: 6, row: "full", label: "Spend tiers",
273
+ when: (v) => v.type === "percentage" || v.type === "fixed",
274
+ help: "Replaces the discount value once the order reaches a threshold. The highest matching tier wins.",
275
+ }),
249
276
  firstTimeUserOnly: annotate(z.boolean(), {
250
277
  section: "scope", order: 3, row: "quarter", label: "First-time users only",
251
278
  }),
@@ -85,6 +85,7 @@ export declare const productDraftSchema: z.ZodObject<{
85
85
  seoTitle: z.ZodOptional<z.ZodString>;
86
86
  seoDescription: z.ZodOptional<z.ZodString>;
87
87
  barcodeId: z.ZodOptional<z.ZodString>;
88
+ sku: z.ZodOptional<z.ZodString>;
88
89
  startingBid: z.ZodOptional<z.ZodNumber>;
89
90
  reservePrice: z.ZodOptional<z.ZodNumber>;
90
91
  buyNowPrice: z.ZodOptional<z.ZodNumber>;
@@ -175,6 +176,7 @@ export declare const productDraftSchema: z.ZodObject<{
175
176
  seoTitle: z.ZodOptional<z.ZodString>;
176
177
  seoDescription: z.ZodOptional<z.ZodString>;
177
178
  barcodeId: z.ZodOptional<z.ZodString>;
179
+ sku: z.ZodOptional<z.ZodString>;
178
180
  startingBid: z.ZodOptional<z.ZodNumber>;
179
181
  reservePrice: z.ZodOptional<z.ZodNumber>;
180
182
  buyNowPrice: z.ZodOptional<z.ZodNumber>;
@@ -265,6 +267,7 @@ export declare const productDraftSchema: z.ZodObject<{
265
267
  seoTitle: z.ZodOptional<z.ZodString>;
266
268
  seoDescription: z.ZodOptional<z.ZodString>;
267
269
  barcodeId: z.ZodOptional<z.ZodString>;
270
+ sku: z.ZodOptional<z.ZodString>;
268
271
  startingBid: z.ZodOptional<z.ZodNumber>;
269
272
  reservePrice: z.ZodOptional<z.ZodNumber>;
270
273
  buyNowPrice: z.ZodOptional<z.ZodNumber>;
@@ -87,6 +87,11 @@ export const productDraftSchema = z
87
87
  seoTitle: z.string().optional(),
88
88
  seoDescription: z.string().optional(),
89
89
  barcodeId: z.string().optional(),
90
+ /*
91
+ * The human stock key. Separate from barcodeId because they are two keys
92
+ * with opposite requirements — see ProductDocument.sku.
93
+ */
94
+ sku: z.string().max(64).optional(),
90
95
  // Auction
91
96
  startingBid: z.number().optional(),
92
97
  reservePrice: z.number().optional(),
@@ -6,7 +6,7 @@ import { Button } from "../../ui/components/Button.js";
6
6
  import { IconButton } from "../../ui/components/IconButton.js";
7
7
  import { classNames } from "../../ui/style.helper.js";
8
8
  import { FORM_ACTION_META, FORM_ACTION_ID } from "../products/constants/action-defs.js";
9
- import { ActionRow, Div, OverflowRow, Row, Span, Stack, Text } from "../../ui/index.js";
9
+ import { ActionRow, Div, Heading, OverflowRow, Row, Span, Stack, Text } from "../../ui/index.js";
10
10
  /**
11
11
  * Carries the `schema` prop passed to `<FormShell>` down to any nested
12
12
  * `<SectionForm>` that doesn't declare its own `schema`/`SectionDef.schema` —
@@ -122,7 +122,7 @@ export function FormShell({ isOpen, onClose, title, breadcrumb, schema, isDirty
122
122
  };
123
123
  if (!isOpen)
124
124
  return null;
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 px-[1.25rem] bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]", padding: "y-xs", children: _jsx(OverflowRow, { items: sections.map((sec) => ({
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(Heading, { level: 1, 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 px-[1.25rem] bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]", padding: "y-xs", children: _jsx(OverflowRow, { items: sections.map((sec) => ({
126
126
  id: sec.id,
127
127
  label: sec.label,
128
128
  onSelect: () => scrollToSection(sec.id),
@@ -203,6 +203,20 @@ export function QuickFormDrawer({ isOpen, onClose, title, fields, defaultValues,
203
203
  submitAttempted,
204
204
  submitAttemptCount,
205
205
  markSubmitAttempted: () => setSubmitAttemptCount((n) => n + 1),
206
+ /*
207
+ * 🛑 Not offered here. This drawer's whole purpose is the 1-3 field
208
+ * inline edit opened from a picker's "+ Create new" — it CLOSES on
209
+ * success and auto-selects what it just made, which is the opposite of
210
+ * staying put. "Add another" would have to mean "create a second one you
211
+ * did not ask for and cannot select", so the control has no coherent
212
+ * meaning in this shell.
213
+ *
214
+ * Also note this context backs ONLY `<FormErrorSummary>` — the comment
215
+ * above says so — so even a `true` here would reach no submit handler.
216
+ */
217
+ addAnotherEnabled: false,
218
+ addAnother: false,
219
+ setAddAnother: () => { },
206
220
  }), [liveErrors, submitting, submitAttempted, submitAttemptCount]);
207
221
  if (!isOpen)
208
222
  return null;
@@ -2,6 +2,7 @@ import { type ReactNode } from "react";
2
2
  import type { JsonValue } from "@mohasinac/appkit/client";
3
3
  import type { ZodTypeAny } from "zod";
4
4
  import { type FormShellStep } from "../../ui/forms/FormShell";
5
+ import { type LabelPlacement } from "../../ui/forms/FieldLabel";
5
6
  /**
6
7
  * A collapsible form segment. Replaces `StepDef` — the differences are the
7
8
  * point, not incidental:
@@ -33,6 +34,24 @@ export interface SectionDef<T extends object = Record<string, JsonValue>> {
33
34
  * `CollapsibleSection.keepMounted`.
34
35
  */
35
36
  keepMounted?: boolean;
37
+ /**
38
+ * How every field in THIS section labels itself: `"above"` (the default),
39
+ * `"inline"`, or `"hidden"`.
40
+ *
41
+ * 🛑 A SECTION-LEVEL DECISION, which is the whole reason it lives here
42
+ * rather than on each field. One field labelling above beside one labelling
43
+ * inline reads as broken rather than as varied, and the choice is really
44
+ * about a section's density — a 12-field address block wants inline, a
45
+ * 3-field settings panel wants above. The field-level prop exists as an
46
+ * escape hatch and should stay unused.
47
+ *
48
+ * `"hidden"` renders the label to assistive technology and hides it visually
49
+ * (`sr-only`, never `display: none`, which would remove it from the
50
+ * accessibility tree and produce the unlabelled field it was meant to
51
+ * avoid). It is for fields whose purpose is obvious from context, NOT a way
52
+ * to suppress the unlabelled-field count.
53
+ */
54
+ labelPlacement?: LabelPlacement;
36
55
  render: (props: {
37
56
  values: T;
38
57
  onChange: (partial: Partial<T>) => void;