@mohasinac/appkit 4.59.0 → 4.60.1

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 (532) 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.d.ts +20 -2
  27. package/dist/_internal/server/features/orders/adapters.js +44 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  29. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  30. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  31. package/dist/_internal/server/features/orders/index.js +1 -1
  32. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  33. package/dist/_internal/server/features/orders/service.js +74 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  35. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  36. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  37. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  38. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  39. package/dist/_internal/server/features/payouts/actions.js +84 -10
  40. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  41. package/dist/_internal/server/features/points/adjust.js +121 -0
  42. package/dist/_internal/server/features/points/award.d.ts +65 -0
  43. package/dist/_internal/server/features/points/award.js +145 -0
  44. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  45. package/dist/_internal/server/features/price-index/market.js +54 -1
  46. package/dist/_internal/server/features/products/data.js +40 -22
  47. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  48. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  49. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  50. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  51. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  52. package/dist/_internal/server/features/refunds/actions.js +259 -17
  53. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  54. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  55. package/dist/_internal/server/features/reviews/actions.js +3 -2
  56. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  57. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  58. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  59. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  60. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  61. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  62. package/dist/_internal/server/features/stores/tabs.js +24 -4
  63. package/dist/_internal/server/functions/https.d.ts +31 -1
  64. package/dist/_internal/server/functions/https.js +52 -1
  65. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  66. package/dist/_internal/server/functions/scheduled.js +124 -1
  67. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  69. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  70. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  72. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  73. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/core/index.js +2 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  76. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  78. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  80. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  81. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  84. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  86. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  87. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  88. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  90. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  92. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  93. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  95. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  96. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  97. package/dist/_internal/shared/actions/action-registry.js +71 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  99. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  100. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  101. package/dist/_internal/shared/checkout/lanes.js +46 -0
  102. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  103. package/dist/_internal/shared/checkout/order-math.js +251 -3
  104. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  105. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  106. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  107. package/dist/_internal/shared/features/auctions/config.js +30 -1
  108. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  109. package/dist/_internal/shared/features/brands/schema.js +18 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  111. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  112. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  113. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  114. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  115. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  116. package/dist/_internal/shared/fees/calculator.js +103 -5
  117. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  118. package/dist/_internal/shared/fees/india-states.js +190 -0
  119. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  120. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  121. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  122. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  123. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  124. package/dist/_internal/shared/shipping/quote.js +67 -0
  125. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  126. package/dist/_internal/shared/time/business-days.js +185 -0
  127. package/dist/cli/index.js +0 -2
  128. package/dist/client.d.ts +10 -4
  129. package/dist/client.js +29 -3
  130. package/dist/configs/tailwind.d.ts +0 -12
  131. package/dist/configs/tailwind.js +28 -15
  132. package/dist/constants/api-endpoints.d.ts +51 -9
  133. package/dist/constants/api-endpoints.js +23 -7
  134. package/dist/constants/index.d.ts +1 -1
  135. package/dist/constants/index.js +1 -1
  136. package/dist/contracts/index.d.ts +1 -1
  137. package/dist/contracts/payment.d.ts +131 -0
  138. package/dist/features/about/components/AboutView.js +1 -1
  139. package/dist/features/about/components/DeveloperView.js +5 -2
  140. package/dist/features/about/components/FAQPageView.js +2 -1
  141. package/dist/features/about/components/FeesView.js +2 -1
  142. package/dist/features/about/components/HelpPageView.js +2 -1
  143. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  144. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  145. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  146. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  147. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  148. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  149. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  150. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  151. package/dist/features/about/components/PolicyPageView.js +2 -1
  152. package/dist/features/about/components/ProseHero.d.ts +73 -0
  153. package/dist/features/about/components/ProseHero.js +18 -0
  154. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  155. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  156. package/dist/features/about/components/TrackOrderView.js +2 -1
  157. package/dist/features/about/components/UnauthorizedView.js +1 -1
  158. package/dist/features/about/index.d.ts +2 -0
  159. package/dist/features/about/index.js +5 -0
  160. package/dist/features/account/actions/address-actions.d.ts +26 -2
  161. package/dist/features/account/actions/address-actions.js +25 -1
  162. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  163. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  164. package/dist/features/account/components/UserOrdersView.js +17 -0
  165. package/dist/features/account/components/UserSidebar.js +1 -1
  166. package/dist/features/account/hooks/useNotifications.js +29 -0
  167. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  168. package/dist/features/admin/components/AdminBeysView.js +2 -2
  169. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  170. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  171. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  175. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  176. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  177. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  178. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  179. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  180. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  181. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  182. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  183. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  184. package/dist/features/admin/components/AdminPartsView.js +2 -2
  185. package/dist/features/admin/components/AdminProductsView.js +75 -4
  186. package/dist/features/admin/components/AdminSidebar.js +2 -2
  187. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  188. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  189. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  190. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  191. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  192. package/dist/features/admin/components/AdminUsersView.js +72 -1
  193. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  194. package/dist/features/admin/components/AdminViewCards.js +4 -4
  195. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  196. package/dist/features/admin/components/DataListingView.js +175 -14
  197. package/dist/features/admin/components/DataTable.d.ts +12 -1
  198. package/dist/features/admin/components/DataTable.js +3 -3
  199. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  200. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  201. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  202. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  203. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  204. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  205. package/dist/features/admin/components/index.d.ts +4 -0
  206. package/dist/features/admin/components/index.js +6 -0
  207. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  208. package/dist/features/admin/constants/filter-tabs.js +106 -0
  209. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  210. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  211. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  212. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  213. package/dist/features/admin/schemas/firestore.js +5 -1
  214. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  215. package/dist/features/auctions/actions/bid-actions.js +13 -1
  216. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  217. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  219. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  220. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  221. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  222. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  223. package/dist/features/audit-log/schemas/firestore.js +14 -0
  224. package/dist/features/auth/permissions/constants.d.ts +17 -1
  225. package/dist/features/auth/permissions/constants.js +18 -0
  226. package/dist/features/blog/components/BlogPostView.js +1 -1
  227. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  228. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  229. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  230. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  231. package/dist/features/categories/components/BundleDetailView.js +1 -1
  232. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  233. package/dist/features/categories/components/CategoryForm.js +12 -1
  234. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  235. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  236. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  237. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  238. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  239. package/dist/features/events/components/EventSaleBanner.js +1 -1
  240. package/dist/features/events/components/EventStatusBadge.js +1 -1
  241. package/dist/features/faq/components/FAQAccordion.js +2 -1
  242. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  243. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  244. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  245. package/dist/features/filters/FilterFacetSection.js +1 -1
  246. package/dist/features/filters/SwitchFilter.js +1 -1
  247. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  248. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  249. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  250. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  251. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  252. package/dist/features/gateway-events/schemas/index.js +79 -0
  253. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  254. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  255. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  256. package/dist/features/homepage/components/BrandsSection.js +1 -1
  257. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  258. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  259. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  260. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  261. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  262. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  264. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  265. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  266. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  267. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  268. package/dist/features/inventory/components/operator-message.js +41 -0
  269. package/dist/features/inventory/index.d.ts +13 -0
  270. package/dist/features/inventory/index.js +28 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  272. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  274. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  276. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  277. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  278. package/dist/features/inventory/schemas/firestore.js +159 -0
  279. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  280. package/dist/features/layout/AppLayoutShell.js +25 -12
  281. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  282. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  283. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  284. package/dist/features/layout/BottomNavbar.js +2 -2
  285. package/dist/features/media/MediaAvatar.js +1 -1
  286. package/dist/features/media/MediaImage.d.ts +24 -4
  287. package/dist/features/media/MediaImage.js +44 -4
  288. package/dist/features/media/MediaLightbox.js +1 -1
  289. package/dist/features/media/modals/ImageCropModal.js +1 -1
  290. package/dist/features/media/modals/ImageEditor.js +1 -1
  291. package/dist/features/media/upload/MediaUploadField.js +91 -3
  292. package/dist/features/obligations/constants/policies.d.ts +92 -0
  293. package/dist/features/obligations/constants/policies.js +378 -0
  294. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  295. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  296. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  297. package/dist/features/obligations/schemas/firestore.js +87 -0
  298. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  299. package/dist/features/orders/actions/order-actions.js +60 -3
  300. package/dist/features/orders/actions/refund-actions.js +70 -10
  301. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  302. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  303. package/dist/features/orders/components/OrdersList.js +2 -2
  304. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  305. package/dist/features/orders/components/index.d.ts +2 -0
  306. package/dist/features/orders/components/index.js +6 -0
  307. package/dist/features/orders/constants/transitions.d.ts +59 -0
  308. package/dist/features/orders/constants/transitions.js +104 -0
  309. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  310. package/dist/features/orders/repository/orders.repository.js +464 -21
  311. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  312. package/dist/features/orders/schemas/firestore.js +24 -0
  313. package/dist/features/orders/schemas/index.d.ts +61 -0
  314. package/dist/features/orders/schemas/index.js +22 -0
  315. package/dist/features/orders/types/index.d.ts +79 -0
  316. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  317. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  318. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  319. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  320. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  322. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  323. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  324. package/dist/features/payments/repository/payout.repository.js +0 -1
  325. package/dist/features/points/repository/points.repository.d.ts +122 -0
  326. package/dist/features/points/repository/points.repository.js +304 -1
  327. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  328. package/dist/features/points/schemas/adjust-form.js +63 -0
  329. package/dist/features/points/schemas/firestore.d.ts +108 -1
  330. package/dist/features/points/schemas/firestore.js +84 -0
  331. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  332. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  333. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  334. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  335. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  336. package/dist/features/price-index/market-stats.d.ts +82 -0
  337. package/dist/features/price-index/market-stats.js +230 -0
  338. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  339. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  340. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  341. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  342. package/dist/features/print/components/labels.d.ts +117 -0
  343. package/dist/features/print/components/labels.js +258 -0
  344. package/dist/features/print/components/labels.style.css +46 -0
  345. package/dist/features/print/index.d.ts +6 -0
  346. package/dist/features/print/index.js +16 -0
  347. package/dist/features/products/columns/productTableColumns.js +5 -4
  348. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  349. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  350. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  351. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  352. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  353. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  354. package/dist/features/products/components/ProductForm.js +34 -23
  355. package/dist/features/products/components/ProductGrid.js +86 -62
  356. package/dist/features/products/constants/card-badges.d.ts +97 -0
  357. package/dist/features/products/constants/card-badges.js +100 -0
  358. package/dist/features/products/repository/products.repository.js +34 -1
  359. package/dist/features/products/schemas/firestore.d.ts +46 -3
  360. package/dist/features/products/schemas/firestore.js +21 -0
  361. package/dist/features/promotions/components/PromotionsView.js +1 -1
  362. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  363. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  364. package/dist/features/promotions/schemas/firestore.js +30 -3
  365. package/dist/features/promotions/schemas/index.d.ts +6 -6
  366. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  367. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  368. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  369. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  370. package/dist/features/reviews/actions/review-actions.js +3 -2
  371. package/dist/features/reviews/components/ReviewModal.js +1 -1
  372. package/dist/features/reviews/components/ReviewsList.js +1 -1
  373. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  374. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  375. package/dist/features/scams/constants/scam-types.js +18 -0
  376. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  377. package/dist/features/scams/schemas/report-form.js +26 -1
  378. package/dist/features/search/components/Search.js +9 -9
  379. package/dist/features/search/components/SearchResultsClient.js +18 -3
  380. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  381. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  382. package/dist/features/seller/actions/offer-actions.js +16 -1
  383. package/dist/features/seller/actions/seller-actions.js +14 -3
  384. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  385. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  386. package/dist/features/seller/components/PrintCenterView.js +41 -30
  387. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  388. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  389. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  390. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  391. package/dist/features/seller/components/SellerProductShell.js +123 -13
  392. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  393. package/dist/features/seller/components/SellerSidebar.js +1 -1
  394. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  395. package/dist/features/seller/schemas/coupon-form.js +27 -0
  396. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  397. package/dist/features/seller/schemas/product-form.js +5 -0
  398. package/dist/features/shell/FormShell.js +2 -2
  399. package/dist/features/shell/QuickFormDrawer.js +14 -0
  400. package/dist/features/shell/SectionForm.d.ts +19 -0
  401. package/dist/features/shell/SectionForm.js +133 -9
  402. package/dist/features/shell/SectionForm.style.css +54 -0
  403. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  404. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  405. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  406. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  407. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  408. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  409. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  410. package/dist/features/stores/components/StoreHeader.js +1 -1
  411. package/dist/features/support/schemas/firestore.d.ts +73 -2
  412. package/dist/features/support/schemas/firestore.js +59 -0
  413. package/dist/index.d.ts +16 -24
  414. package/dist/index.js +38 -47
  415. package/dist/next/ErrorBoundary.js +1 -1
  416. package/dist/next/routing/route-map.d.ts +24 -0
  417. package/dist/next/routing/route-map.js +40 -0
  418. package/dist/providers/payment-manual/index.d.ts +15 -1
  419. package/dist/providers/payment-manual/index.js +37 -0
  420. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  421. package/dist/providers/payment-phonepe/index.js +36 -0
  422. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  423. package/dist/providers/payment-razorpay/index.js +99 -0
  424. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  425. package/dist/providers/shipping-shiprocket/index.js +18 -0
  426. package/dist/schemas/registry.d.ts +44 -11
  427. package/dist/server-entry.d.ts +2 -0
  428. package/dist/server-entry.js +27 -0
  429. package/dist/server.d.ts +43 -0
  430. package/dist/server.js +98 -0
  431. package/dist/styles.css +418 -149
  432. package/dist/tailwind-utilities.css +1 -1
  433. package/dist/tokens/themes/default-dark.js +15 -12
  434. package/dist/tokens/themes/default-light.js +14 -11
  435. package/dist/tokens/tokens.css +106 -23
  436. package/dist/ui/DataTable.style.css +4 -4
  437. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  438. package/dist/ui/columns/cell-renderers.js +26 -1
  439. package/dist/ui/columns/index.d.ts +1 -1
  440. package/dist/ui/columns/index.js +4 -1
  441. package/dist/ui/components/Badge.style.css +1 -1
  442. package/dist/ui/components/Breadcrumb.js +19 -1
  443. package/dist/ui/components/BulkActionBar.style.css +4 -4
  444. package/dist/ui/components/Button.style.css +1 -1
  445. package/dist/ui/components/Checkbox.style.css +1 -1
  446. package/dist/ui/components/FallbackShell.js +1 -1
  447. package/dist/ui/components/Form.d.ts +14 -1
  448. package/dist/ui/components/Form.js +2 -2
  449. package/dist/ui/components/FormField.js +18 -4
  450. package/dist/ui/components/FormField.style.css +43 -3
  451. package/dist/ui/components/GatedPrice.style.css +1 -1
  452. package/dist/ui/components/IconButton.style.css +1 -1
  453. package/dist/ui/components/Input.js +22 -1
  454. package/dist/ui/components/Input.style.css +38 -22
  455. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  456. package/dist/ui/components/ListingToolbar.js +1 -1
  457. package/dist/ui/components/PaginatedSelect.js +0 -0
  458. package/dist/ui/components/Radio.style.css +1 -1
  459. package/dist/ui/components/Repeater.d.ts +53 -0
  460. package/dist/ui/components/Repeater.js +7 -0
  461. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  462. package/dist/ui/components/RichTextEditor.js +23 -2
  463. package/dist/ui/components/Select.style.css +7 -21
  464. package/dist/ui/components/Semantic.js +27 -3
  465. package/dist/ui/components/TagInput.style.css +4 -9
  466. package/dist/ui/components/Textarea.style.css +4 -18
  467. package/dist/ui/components/Toggle.style.css +1 -1
  468. package/dist/ui/components/Typography.d.ts +3 -3
  469. package/dist/ui/components/Typography.js +15 -15
  470. package/dist/ui/components/ViewToggle.style.css +4 -4
  471. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  472. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  473. package/dist/ui/forms/ColorPickerField.js +15 -2
  474. package/dist/ui/forms/FieldCheckbox.js +15 -2
  475. package/dist/ui/forms/FieldInput.d.ts +22 -1
  476. package/dist/ui/forms/FieldInput.js +19 -5
  477. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  478. package/dist/ui/forms/FieldLabel.js +70 -0
  479. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  480. package/dist/ui/forms/FieldSelect.js +27 -5
  481. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  482. package/dist/ui/forms/FieldTextarea.js +22 -5
  483. package/dist/ui/forms/FormShell.d.ts +17 -1
  484. package/dist/ui/forms/FormShell.js +36 -2
  485. package/dist/ui/forms/FormShell.style.css +41 -33
  486. package/dist/ui/forms/index.d.ts +5 -1
  487. package/dist/ui/forms/index.js +2 -0
  488. package/dist/ui/icons/icon-registry.d.ts +1 -0
  489. package/dist/ui/icons/icon-registry.js +17 -1
  490. package/dist/utils/id-generators.d.ts +80 -0
  491. package/dist/utils/id-generators.js +93 -0
  492. package/dist/utils/search-txt-builders.d.ts +12 -0
  493. package/dist/utils/search-txt-builders.js +17 -0
  494. package/package.json +1 -1
  495. package/scripts/seed-cli.mjs +106 -5
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  497. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  498. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  499. package/dist/features/loyalty/api/balance/route.js +0 -41
  500. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  501. package/dist/features/loyalty/api/earn/route.js +0 -38
  502. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  503. package/dist/features/loyalty/api/history/route.js +0 -58
  504. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  505. package/dist/features/loyalty/api/redeem/route.js +0 -38
  506. package/dist/features/loyalty/columns/index.d.ts +0 -17
  507. package/dist/features/loyalty/columns/index.js +0 -40
  508. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  509. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  510. package/dist/features/loyalty/components/index.d.ts +0 -1
  511. package/dist/features/loyalty/components/index.js +0 -1
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  513. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  514. package/dist/features/loyalty/index.d.ts +0 -6
  515. package/dist/features/loyalty/index.js +0 -6
  516. package/dist/features/loyalty/manifest.d.ts +0 -2
  517. package/dist/features/loyalty/manifest.js +0 -16
  518. package/dist/features/loyalty/messages/en.json +0 -19
  519. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  520. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  521. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  522. package/dist/features/loyalty/schemas/index.js +0 -40
  523. package/dist/features/loyalty/server.d.ts +0 -6
  524. package/dist/features/loyalty/server.js +0 -6
  525. package/dist/features/loyalty/types/index.d.ts +0 -31
  526. package/dist/features/loyalty/types/index.js +0 -1
  527. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  528. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  529. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  530. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  531. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  532. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -1,7 +1,30 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * DataListingView — config-driven listing scaffold for admin + seller dashboards.
5
+ *
6
+ * Replaces ~30 hand-wired admin/seller view components (AdminBrandsView, AdminCategoriesView,
7
+ * SellerCouponsView, etc.) by accepting a single `config` object describing:
8
+ * - data layer (endpoint, queryKey, mapRows, buildFilters, sortOptions)
9
+ * - presentation (title, subtitle, columns, searchPlaceholder, emptyLabel)
10
+ * - actions (bulkActions, primaryAction, renderRowActions)
11
+ * - filter panel (renderFilterPanel)
12
+ * - editor side-drawer (renderEditor)
13
+ *
14
+ * Composes: useAdminListing (data + state) + ListingToolbar + BulkActionBar + Pagination
15
+ * + DataTable | AdminViewCards + ListingFilterDrawer + SideDrawer
16
+ * + useBottomActions (mobile bulk bar)
17
+ *
18
+ * Usage:
19
+ * const ADMIN_BRANDS_CONFIG: ListingViewConfig<BrandsResponse, BrandRow> = { ... };
20
+ * <DataListingView config={ADMIN_BRANDS_CONFIG} />
21
+ *
22
+ * Created 2026-05-23 as part of W6-12 (plan: read-this-files-and-modular-storm.md).
23
+ */
24
+ import React from "react";
3
25
  import { Plus } from "lucide-react";
4
- import { BulkActionBar, Button, Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, SideDrawer, StickyToolbar, TextLink } from "../../../ui/index.js";
26
+ import { BulkActionBar, Button, Div, Heading, Icon, IconButton, ListingFilterDrawer, ListingToolbar, Pagination, Row, SideDrawer, Stack, StickyToolbar, Text, TextLink } from "../../../ui/index.js";
27
+ import { ROW_ACTION_ID, ROW_ACTION_META } from "../../products/constants/action-defs.js";
5
28
  import { useBottomActions } from "../../layout/index.js";
6
29
  import { useAdminListing } from "../hooks/useAdminListing.js";
7
30
  import { getResourceIcon } from "../../../ui/columns/column-renderers.js";
@@ -44,6 +67,55 @@ export function DataListingView({ config, }) {
44
67
  openEditPanel: panel.openEditPanel,
45
68
  };
46
69
  const resourceIcon = getResourceIcon(typeof config.queryKey[1] === "string" ? config.queryKey[1] : undefined);
70
+ /**
71
+ * ONE renderer for `config.primaryAction`, used by the toolbar AND by the
72
+ * empty state.
73
+ *
74
+ * 🛑 WHY THIS EXISTS: the action was rendered in the toolbar and nowhere
75
+ * else, so an admin landing on an EMPTY list was told "No X found" with no
76
+ * way forward — while the button that creates the first record sat in a
77
+ * toolbar above an empty table. 27 of ~84 listing configs declare a
78
+ * `primaryAction`; all 27 had that hole.
79
+ *
80
+ * 🛑 And it is a FUNCTION rather than two copies, because the alternative is
81
+ * the shape Root Cause #75 is about: a rule spelled out twice, where the
82
+ * copy made first learns nothing when the other gains a branch. The `href`
83
+ * vs `onClick` split and the `asChild` anchor are exactly the sort of detail
84
+ * that would drift.
85
+ *
86
+ * `size` is the only difference between the two placements — a toolbar
87
+ * button is `sm`, and an empty state is the one thing on screen, so it gets
88
+ * the default.
89
+ */
90
+ /*
91
+ * 🛑 TWO DIFFERENT EMPTY STATES, and offering the wrong action is worse than
92
+ * offering none.
93
+ *
94
+ * "No products found" with a New Product button is right on a fresh install
95
+ * and actively misleading when the admin has just filtered to
96
+ * `status=archived` — there may be a thousand products, and the useful move
97
+ * is to clear the filter, not to create a thousand-and-first. `resetAll`
98
+ * and `hasActiveState` already exist for the toolbar's own Reset control,
99
+ * so this needs no new state and cannot disagree with it.
100
+ */
101
+ const emptyIsFiltered = hasActiveState;
102
+ const emptyLabel = config.emptyLabel ?? `No ${config.title.toLowerCase()} found`;
103
+ const resolvedEmptyLabel = emptyIsFiltered
104
+ ? `No ${config.title.toLowerCase()} match these filters`
105
+ : emptyLabel;
106
+ const renderPrimaryAction = (size) => {
107
+ const action = config.primaryAction;
108
+ if (!action)
109
+ return null;
110
+ const icon = action.icon ?? _jsx(Plus, { className: "h-4 w-4" });
111
+ return action.href ? (
112
+ // `asChild` so a create PAGE is a real anchor — middle-click and
113
+ // copy-link work, and it is not a button that happens to navigate (a
114
+ // Known-TS-Pattern this repo bans outright).
115
+ _jsx(Button, { gap: "sm", size: size, asChild: true, children: _jsxs(TextLink, { variant: "inherit", href: action.href, children: [icon, action.label] }) })) : (_jsxs(Button, { gap: "sm", size: size, onClick: () => action.onClick?.({ openCreatePanel: panel.openCreatePanel }), children: [icon, action.label] }));
116
+ };
117
+ /** What an empty list offers: clear the filters, or create the first record. */
118
+ const renderEmptyAction = () => emptyIsFiltered ? (_jsx(Button, { variant: "secondary", onClick: resetAll, children: "Clear filters" })) : (renderPrimaryAction());
47
119
  const bulkActionItems = config.buildBulkActions?.(selectionContext);
48
120
  // Mobile bulk bar
49
121
  useBottomActions(selection.selectedCount > 0 && bulkActionItems
@@ -85,20 +157,109 @@ export function DataListingView({ config, }) {
85
157
  window.location.href = href;
86
158
  }
87
159
  : undefined;
160
+ /*
161
+ * ── A DIRECT EDIT AFFORDANCE ON EVERY ROW ─────────────────────────────────
162
+ *
163
+ * Measured across 16 admin and store listings: ZERO edit links in rows and
164
+ * zero pencils on admin. Editing meant opening a `⋯` menu first — two
165
+ * clicks and a hidden menu for the single most common action an admin
166
+ * performs. `/store/products` had 15 pencil-labelled buttons and admin had
167
+ * none, so the two halves of the product did not even agree.
168
+ *
169
+ * Composed here rather than added to each config because ~105 listing
170
+ * screens render through this one component, and both row renderers
171
+ * (`DataTable` and `AdminViewCards`) already drop `renderRowActions` into a
172
+ * click-stopped container — so one wrapper reaches every surface.
173
+ *
174
+ * 🛑 ONLY WHEN AN EDIT TARGET ACTUALLY RESOLVES. This file already carries a
175
+ * long note about `AdminViewCards` once wiring every card to
176
+ * `openEditPanel` whether or not the config had anywhere to go, so cards
177
+ * looked clickable and silently did nothing. A pencil that opens nothing is
178
+ * that same lie with a sharper point on it: the two real targets are a
179
+ * drawer (`renderEditor`) and a route (`rowHrefTemplate`), and a config with
180
+ * neither keeps exactly the actions it declared.
181
+ *
182
+ * 🛑 `onRowClick` is deliberately NOT a target. It is an arbitrary callback
183
+ * — several configs use it to open a *view* modal, toggle a selection, or
184
+ * navigate somewhere that is not an editor — so labelling it "Edit" would
185
+ * promise one thing and do another.
186
+ *
187
+ * Label and icon come from `ROW_ACTION_META[ROW_ACTION_ID.EDIT]` per Rule
188
+ * #7, so this shares one definition with every `⋯` menu's own Edit entry
189
+ * rather than hardcoding a second "Edit" string.
190
+ */
191
+ const rowEditHref = React.useCallback((row) => {
192
+ const t = config.rowHrefTemplate;
193
+ if (!t)
194
+ return null;
195
+ return typeof t === "function"
196
+ ? t(row)
197
+ : t.replace("{id}", encodeURIComponent(row.id));
198
+ }, [config.rowHrefTemplate]);
199
+ const hasEditTarget = Boolean(config.renderEditor ?? config.rowHrefTemplate);
200
+ /*
201
+ * The row's own name for the accessible label, falling back to its id.
202
+ *
203
+ * 🛑 A RUNTIME CHECK, not a cast. `TRow` is only `{ id: string }` here, but
204
+ * every config maps its rows through `mapRows` into `AdminDataRow`, so
205
+ * `primary` is present in practice — which is exactly the situation where
206
+ * `(row as AdminDataRow).primary` would compile and then read `undefined`
207
+ * on the one config that is shaped differently. `typeof` proves it instead.
208
+ *
209
+ * It matters because the alternative is ~50 buttons on a page all announcing
210
+ * "Edit product": a screen-reader user hears the control and not the record,
211
+ * which is the same as having no label at all.
212
+ */
213
+ const rowEditLabel = React.useCallback((row) => {
214
+ const primary = row.primary;
215
+ const name = typeof primary === "string" && primary.trim() ? primary.trim() : row.id;
216
+ return `${ROW_ACTION_META[ROW_ACTION_ID.EDIT].label} ${name}`;
217
+ }, []);
218
+ /*
219
+ * Hoisted so the memo below depends on plain identifiers.
220
+ * `react-hooks/exhaustive-deps` cannot track a property read inside the
221
+ * nested function the memo RETURNS, so reading `config.x` there asks for
222
+ * `config` itself as a dependency — and `config` is a fresh object on every
223
+ * render for every inline config, which would make the memo recompute each
224
+ * time and hand `DataTable` a new prop identity on every render.
225
+ */
226
+ const { renderEditor, renderRowActions } = config;
227
+ const { openEditPanel } = panel;
228
+ const renderRowActionsWithEdit = React.useMemo(() => {
229
+ if (!hasEditTarget)
230
+ return renderRowActions;
231
+ return (row) => {
232
+ const href = rowEditHref(row);
233
+ return (_jsxs(Row, { align: "center", justify: "end", gap: "xs", children: [renderEditor ? (_jsx(IconButton, { "aria-label": rowEditLabel(row), size: "sm", variant: "ghost", icon: _jsx(Icon, { name: "edit" }), onClick: () => openEditPanel(row.id) })) : href ? (
234
+ /*
235
+ * A real link, not a click handler calling `window.location`.
236
+ * The row's own navigation still does that — changing it is the
237
+ * Phase 3 chrome work — but a NEW affordance has no reason to
238
+ * inherit a full page load, and a link is also middle-clickable,
239
+ * which is how an admin opens three records to compare them.
240
+ *
241
+ * Conditional rather than falling back to `href="#"`: the branch
242
+ * already guarantees a template exists, so a null here would mean
243
+ * one returned nothing — and a pencil linking to the current page
244
+ * is worse than no pencil.
245
+ */
246
+ _jsx(TextLink, { href: href, variant: "bare", "aria-label": rowEditLabel(row), children: _jsx(Icon, { name: "edit", size: "sm" }) })) : null, renderRowActions?.(row)] }));
247
+ };
248
+ }, [
249
+ hasEditTarget,
250
+ renderRowActions,
251
+ renderEditor,
252
+ rowEditHref,
253
+ rowEditLabel,
254
+ openEditPanel,
255
+ ]);
88
256
  // A box exists only when the config asks for one.
89
257
  const searchEnabled = Boolean(config.search ?? config.searchPlaceholder);
90
- return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.search?.placeholder ?? config.searchPlaceholder,
258
+ return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [!config.hideHeader && (_jsxs(Stack, { gap: "xs", className: "mb-[var(--appkit-space-4)]", children: [_jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: config.title }), config.subtitle && (_jsx(Text, { size: "sm", color: "muted", children: config.subtitle }))] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.search?.placeholder ?? config.searchPlaceholder,
91
259
  // Passing this is what renders the box — ListingToolbar gates on it.
92
260
  // Undefined when the config declares no search, so a view whose
93
261
  // endpoint cannot search no longer shows an input that does nothing.
94
- onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, config.primaryAction &&
95
- (config.primaryAction.href ? (
96
- // `asChild` so the create PAGE is a real anchor — middle-click
97
- // and copy-link work, and it is not a button that happens to
98
- // navigate (a Known-TS-Pattern this repo bans outright).
99
- _jsx(Button, { gap: "sm", size: "sm", asChild: true, children: _jsxs(TextLink, { variant: "inherit", href: config.primaryAction.href, children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] }) })) : (_jsxs(Button, { gap: "sm", size: "sm", onClick: () => config.primaryAction.onClick?.({
100
- openCreatePanel: panel.openCreatePanel,
101
- }), children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] })))] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
262
+ onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, renderPrimaryAction("sm")] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: resolvedEmptyLabel, emptyAction: renderEmptyAction(), selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
102
263
  ? selection.setSelectedIds(rows.map((r) => r.id))
103
264
  : selection.clearSelection(), rowHrefTemplate: config.rowHrefTemplate, onRowClick: config.onRowClick
104
265
  ? (row) => config.onRowClick(row, {
@@ -106,14 +267,14 @@ export function DataListingView({ config, }) {
106
267
  })
107
268
  : config.renderEditor
108
269
  ? (row) => panel.openEditPanel(row.id)
109
- : undefined, renderRowActions: config.renderRowActions })) : config.renderCards ? (
270
+ : undefined, renderRowActions: renderRowActionsWithEdit })) : config.renderCards ? (
110
271
  // The persisted view-mode preference is global, so a "table"
111
272
  // value can still reach here on a hideTableView-only view (see
112
273
  // the DataTable branch's guard above) — fall back to "grid".
113
- config.renderCards(rows, view === "table" ? "grid" : view, selectionContext, isLoading)) : (_jsx(AdminViewCards, { rows: rows, view: view === "table" ? "grid" : view, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, onRowClick: resolvedRowClick
274
+ config.renderCards(rows, view === "table" ? "grid" : view, selectionContext, isLoading)) : (_jsx(AdminViewCards, { rows: rows, view: view === "table" ? "grid" : view, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: resolvedEmptyLabel, emptyAction: renderEmptyAction(), onRowClick: resolvedRowClick
114
275
  ? (row) => resolvedRowClick(row)
115
- : undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle, renderRowActions: config.renderRowActions
116
- ? (row) => config.renderRowActions(row)
276
+ : undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle, renderRowActions: renderRowActionsWithEdit
277
+ ? (row) => renderRowActionsWithEdit(row)
117
278
  : undefined }))] }), config.renderFilterPanel && (_jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: config.renderFilterPanel({
118
279
  pendingFilters,
119
280
  setPendingFilters,
@@ -24,6 +24,17 @@ interface DataTableProps<T extends {
24
24
  currentPage?: number;
25
25
  onPageChange?: (page: number) => void;
26
26
  emptyLabel?: string;
27
+ /**
28
+ * Rendered beneath `emptyLabel` when there are no rows — normally the
29
+ * listing's own create button.
30
+ *
31
+ * 🛑 A ReactNode rather than `{label, href}`: the caller already owns a
32
+ * renderer for its primary action, and a second description of the same
33
+ * button here would be two things to keep in step. Optional, because a
34
+ * listing with nothing to offer should say so and stop — an empty state
35
+ * with a fabricated action is worse than a quiet one.
36
+ */
37
+ emptyAction?: React.ReactNode;
27
38
  /** When provided, renders a leading checkbox column + long-press to toggle. */
28
39
  selectedIds?: Set<string>;
29
40
  onToggleSelect?: (id: string, selected: boolean) => void;
@@ -33,5 +44,5 @@ interface DataTableProps<T extends {
33
44
  }
34
45
  export declare function DataTable<T extends {
35
46
  id: string;
36
- }>({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages, currentPage, onPageChange, emptyLabel, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }: DataTableProps<T>): React.JSX.Element;
47
+ }>({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages, currentPage, onPageChange, emptyLabel, emptyAction, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }: DataTableProps<T>): React.JSX.Element;
37
48
  export {};
@@ -48,11 +48,11 @@ function SelectableRow({ row, columns, isSelected, onToggle, renderRowActions, o
48
48
  ? col.render(row)
49
49
  : String(row[col.key] ?? "") }, col.key))), renderRowActions && (_jsx(Td, { padding: "xs-3", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
50
50
  }
51
- export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No records found", rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }) {
51
+ export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No records found", emptyAction, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }) {
52
52
  const columns = (columnsProp ?? buildDefaultColumns(resourceIcon));
53
53
  const selectionEnabled = Boolean(onToggleSelect);
54
54
  const allRowsSelected = selectionEnabled && rows.length > 0 && rows.every((r) => selectedIds?.has(r.id));
55
- return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)] ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", weight: "semibold", children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx(Th, { padding: "xs-3", scope: "col", className: "w-12" })] }) }), _jsx(Tbody, { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs(Tr, { border: "default", children: [selectionEnabled && _jsx(Td, { padding: "xs-3", className: "w-10" }), columns.map((col) => (_jsx(Td, { padding: "md", className: COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)], children: _jsx(Div, { className: "h-4 w-full animate-pulse bg-neutral-200", rounded: "default" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx(Tr, { children: _jsx(Td, { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), align: "center", color: "faint", paddingX: "md", paddingY: "md", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: typeof rowHrefTemplate === "function"
55
+ return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)] ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", weight: "semibold", children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx(Th, { padding: "xs-3", scope: "col", className: "w-12" })] }) }), _jsx(Tbody, { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs(Tr, { border: "default", children: [selectionEnabled && _jsx(Td, { padding: "xs-3", className: "w-10" }), columns.map((col) => (_jsx(Td, { padding: "md", className: COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)], children: _jsx(Div, { className: "h-4 w-full animate-pulse bg-neutral-200", rounded: "default" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx(Tr, { children: _jsx(Td, { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), align: "center", color: "faint", paddingX: "md", paddingY: "md", children: _jsxs(Stack, { paddingY: "y-xl", gap: "md", align: "center", children: [_jsx(Text, { size: "sm", color: "muted", children: emptyLabel }), emptyAction] }) }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: typeof rowHrefTemplate === "function"
56
56
  ? rowHrefTemplate(row)
57
- : rowHrefTemplate?.replace("{id}", encodeURIComponent(row.id)), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Row, { className: "border-t border-neutral-200", padding: "inline", align: "center", justify: "end", gap: "sm", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-[length:var(--appkit-text-xs)] font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: p }, p))) }))] }));
57
+ : rowHrefTemplate?.replace("{id}", encodeURIComponent(row.id)), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Row, { className: "border-t border-neutral-200", padding: "inline", align: "center", justify: "end", gap: "sm", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-[length:var(--appkit-text-xs)] font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: p }, p))) }))] }));
58
58
  }
@@ -1,5 +1,5 @@
1
1
  /** Which registry bucket supplies the labels and the confirm copy. */
2
- export type OwnerQuickActionsResource = "PRODUCT" | "CATEGORY" | "BRAND" | "FRANCHISE" | "PART" | "BEY" | "BUNDLE" | "GROUP" | "SUBLISTING" | "EVENT" | "BLOG" | "REVIEW" | "STORE" | "USER" | "CAROUSEL" | "SECTION";
2
+ export type OwnerQuickActionsResource = "PRODUCT" | "CATEGORY" | "BRAND" | "FRANCHISE" | "PART" | "BEY" | "FAQ" | "BUNDLE" | "GROUP" | "SUBLISTING" | "EVENT" | "BLOG" | "REVIEW" | "STORE" | "USER" | "CAROUSEL" | "SECTION";
3
3
  export interface OwnerQuickActionsProps {
4
4
  resource: OwnerQuickActionsResource;
5
5
  /** Where the pencil goes — that record's dashboard editor. */
@@ -66,6 +66,12 @@ const RESOURCE_LEAVES = {
66
66
  */
67
67
  PART: { bucket: "PART", edit: "edit", remove: "delete" },
68
68
  BEY: { bucket: "BEY", edit: "edit", remove: "delete" },
69
+ /*
70
+ * No `duplicate`: two identical questions in a help centre is a defect, not
71
+ * a shortcut — and unlike a product there is no per-copy field (price,
72
+ * stock, barcode) that makes a near-duplicate useful.
73
+ */
74
+ FAQ: { bucket: "FAQ", edit: "edit", remove: "delete" },
69
75
  BRAND: { bucket: "BRAND", edit: "edit", remove: "delete" },
70
76
  BUNDLE: { bucket: "BUNDLE", edit: "edit", remove: "delete" },
71
77
  GROUP: { bucket: "GROUP", edit: "edit", remove: "delete" },
@@ -183,7 +189,18 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
183
189
  * therefore rendered near-black on near-black and read as "admins get no
184
190
  * edit button at all". See `onMedia` above and Root Cause #67.
185
191
  */
186
- const onDark = placement === "card" || onMedia;
192
+ /*
193
+ * 🛑 `onMedia` ALONE now — `placement === "card"` no longer implies a dark
194
+ * backdrop and including it would be actively wrong.
195
+ *
196
+ * It used to, because every card pinned this toolbar over its hero
197
+ * PHOTOGRAPH, so "card" and "on media" were the same thing. The card
198
+ * toolbars now sit in normal flow at the foot of the card, on the card's own
199
+ * surface — where a dark scrim with white icons is a black rectangle on a
200
+ * white card. A caller that genuinely still renders over artwork passes
201
+ * `onMedia` explicitly, which is what that prop is for.
202
+ */
203
+ const onDark = onMedia;
187
204
  const neutralVariant = onDark ? "scrim" : "ghost";
188
205
  /*
189
206
  * Danger ink is dropped on a dark backdrop for the same reason: `danger`
@@ -192,7 +209,48 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
192
209
  * actually communicated — colour was never the safeguard.
193
210
  */
194
211
  const dangerVariant = onDark ? "scrim" : "danger";
195
- return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: className,
212
+ /*
213
+ * ── THE BOX ───────────────────────────────────────────────────────────────
214
+ *
215
+ * 🛑 THE TOOLBAR NOW CARRIES ITS OWN CONTAINER, AND THAT IS THE FIX FOR
216
+ * "they appear at random places".
217
+ *
218
+ * This component used to render a BARE `<Row>` and leave every decision
219
+ * about where it sat — and whether it read as a group at all — to each
220
+ * caller's `className`. Measured, the callers disagreed completely: the
221
+ * product grid pinned it `absolute right-2 top-14`, i.e. ON the product
222
+ * photo; the pre-order card landed it over its own "Reserve now" CTA; the
223
+ * category hero put it ABOVE the `<h1>`, overlapping the title. Three
224
+ * surfaces, three positions, none of them legible as a toolbar.
225
+ *
226
+ * A shared control cluster needs a shared container, or the next surface
227
+ * invents a fourth position. So the box is here, once:
228
+ *
229
+ * - a real surface and border, so it reads as a discrete group of
230
+ * controls rather than three icons floating on artwork;
231
+ * - `rounded` + padding, so the icons have breathing room and the hit
232
+ * targets do not touch;
233
+ * - a shadow, which is what separates it from whatever it sits on when
234
+ * the backdrop happens to be a similar tone.
235
+ *
236
+ * 🛑 `inline-flex`, never `w-full`. In normal flow this must shrink to its
237
+ * own content — a full-width bar would read as a section divider and would
238
+ * stretch across a card body it is only a corner of.
239
+ */
240
+ const boxClass = onDark
241
+ ? /*
242
+ * On media: a translucent dark scrim rather than a theme surface. The
243
+ * `frost` reasoning from Root Cause #67 — a theme-relative surface is a
244
+ * LIGHT background in every light theme, so a themed box on a dark hero
245
+ * photo is a white rectangle over the artwork.
246
+ */
247
+ "inline-flex rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
248
+ : /*
249
+ * Off media: the real surface tokens, so it inverts with the theme
250
+ * instead of being a fixed near-white that disappears in dark mode.
251
+ */
252
+ "inline-flex rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
253
+ return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: `${boxClass} ${className}`.trim(),
196
254
  /*
197
255
  * Stops the click reaching an ancestor `<Link>` or the card's own
198
256
  * `onClick`. Without it, pressing Edit on a card both navigates to the
@@ -8,7 +8,7 @@
8
8
  * leaf there still renders no bin — the resource owns that decision, which is
9
9
  * the point of gating it in two places.
10
10
  */
11
- export type RecordQuickActionResource = "CATEGORY" | "BRAND" | "BUNDLE" | "GROUP" | "EVENT" | "BLOG" | "FRANCHISE" | "SUBLISTING" | "PART" | "BEY";
11
+ export type RecordQuickActionResource = "CATEGORY" | "BRAND" | "BUNDLE" | "GROUP" | "EVENT" | "BLOG" | "FRANCHISE" | "SUBLISTING" | "PART" | "BEY" | "FAQ" | "PRODUCT";
12
12
  export interface RecordOwnerQuickActionsProps {
13
13
  resource: RecordQuickActionResource;
14
14
  /** The record's document id — what the endpoint and the editor are keyed on. */
@@ -12,9 +12,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  *
13
13
  * Event handlers cannot be passed to Client Component props.
14
14
  *
15
- * at render. `ProductOwnerQuickActions` records that this exact mistake took
16
- * down every `/products/[slug]` page in production, with an opaque digest per
17
- * handler.
15
+ * at render. The now-deleted product-specific wrapper recorded that this exact
16
+ * mistake took down every `/products/[slug]` page in production, with an
17
+ * opaque digest per handler.
18
18
  *
19
19
  * That is why ten detail views were passing `editHref` and nothing else: the
20
20
  * bin and the clone are rendered behind `{showDelete && …}` / `{showClone && …}`,
@@ -23,20 +23,26 @@ import { jsx as _jsx } from "react/jsx-runtime";
23
23
  *
24
24
  * ## 🛑 Why ONE wrapper and not one per resource
25
25
  *
26
- * `ProductOwnerQuickActions` is resource-specific because products are the one
27
- * collection a SELLER can also own, so its endpoints live under
28
- * `SELLER_ENDPOINTS` and its clone has a bespoke redirect to the copy's editor.
29
- * Everything here is admin-owned and differs only by three strings, so eight
30
- * near-identical files would trip the Duplication Framework's Rule of Three
31
- * immediately. One table, one component.
26
+ * There used to be TWO wrappers — this one and `ProductOwnerQuickActions` —
27
+ * because a product is the only collection a SELLER can also own, so it needs
28
+ * `SELLER_ENDPOINTS`, a clone that lands on the copy's editor, and a
29
+ * post-delete target that is not an admin list.
30
+ *
31
+ * 🛑 THOSE THREE DIFFERENCES ARE NOW ROWS IN THE TABLE, NOT A SECOND FILE.
32
+ * Every one of them is real and every one survives — but none needed a
33
+ * separate component to express, and having two meant two places for the box,
34
+ * the placement and the visibility rule to drift. That drift is exactly what
35
+ * shipped: five card types pinned the toolbar to four different corners, one
36
+ * of them on top of its own CTA, and the category hero rendered it over the
37
+ * `<h1>`. One table, one component, one position everywhere.
32
38
  *
33
39
  * ## Why the redirect lands on the ADMIN list
34
40
  *
35
41
  * You deleted the record from its own public page, so that page is now a 404 —
36
42
  * staying is not an option and the public index tells you nothing about whether
37
43
  * the delete worked. The admin list is where the absence is verifiable.
38
- * `ProductOwnerQuickActions` differs deliberately: a seller may delete a
39
- * product and has no admin list to land on.
44
+ * PRODUCT differs deliberately, through its own `afterDelete`: a seller may
45
+ * delete a product and has no admin list to land on.
40
46
  *
41
47
  * @tag domain:admin
42
48
  * @tag layer:components
@@ -44,7 +50,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
44
50
  */
45
51
  import { useCallback } from "react";
46
52
  import { OwnerQuickActions } from "./OwnerQuickActions.js";
47
- import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
53
+ import { ADMIN_ENDPOINTS, SELLER_ENDPOINTS } from "../../../constants/api-endpoints.js";
48
54
  import { ROUTES } from "../../../next/routing/route-map.js";
49
55
  const WIRING = {
50
56
  CATEGORY: {
@@ -97,6 +103,18 @@ const WIRING = {
97
103
  editHref: (id) => String(ROUTES.ADMIN.CATEGORIES_EDIT(id)),
98
104
  afterDelete: String(ROUTES.ADMIN.CATEGORIES),
99
105
  },
106
+ PRODUCT: {
107
+ endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
108
+ editHref: (id) => String(ROUTES.STORE.PRODUCTS_EDIT(id)),
109
+ afterDelete: String(ROUTES.STORE.PRODUCTS),
110
+ clone: SELLER_ENDPOINTS.PRODUCT_DUPLICATE,
111
+ afterClone: (newId) => String(ROUTES.STORE.PRODUCTS_EDIT(newId)),
112
+ },
113
+ FAQ: {
114
+ endpoint: ADMIN_ENDPOINTS.FAQ_BY_ID,
115
+ editHref: (id) => String(ROUTES.ADMIN.FAQS_EDIT(id)),
116
+ afterDelete: String(ROUTES.ADMIN.FAQS),
117
+ },
100
118
  SUBLISTING: {
101
119
  endpoint: ADMIN_ENDPOINTS.SUBLISTING_CATEGORY_BY_ID,
102
120
  editHref: (id) => String(ROUTES.ADMIN.SUBLISTING_CATEGORIES_EDIT(id)),
@@ -112,7 +130,7 @@ export function RecordOwnerQuickActions({ resource, recordId, recordLabel, owner
112
130
  * navigates away — so a swallowed failure is indistinguishable from "it
113
131
  * worked but the page did not move", and the obvious user response is to
114
132
  * press the button again. Returning early on `!res.ok` is the bug
115
- * `ProductOwnerQuickActions` documents having had.
133
+ * the deleted product wrapper documented having had.
116
134
  */
117
135
  const handleDelete = useCallback(async () => {
118
136
  const res = await fetch(wiring.endpoint(recordId), { method: "DELETE" });
@@ -124,5 +142,34 @@ export function RecordOwnerQuickActions({ resource, recordId, recordLabel, owner
124
142
  window.location.assign(wiring.afterDelete);
125
143
  }
126
144
  }, [wiring, recordId]);
127
- return (_jsx(OwnerQuickActions, { resource: resource, placement: placement, onMedia: onMedia, className: className, recordLabel: recordLabel, ownerId: ownerId, storeId: storeId, editHref: wiring.editHref(recordId), onDelete: handleDelete }));
145
+ /*
146
+ * Clone, for the resources whose table entry declares one.
147
+ *
148
+ * 🛑 Built UNCONDITIONALLY — the hook cannot be called behind an `if` — but
149
+ * passed to `OwnerQuickActions` only when `wiring.clone` exists, which is
150
+ * what makes the button absent rather than present-and-broken for the
151
+ * resources that cannot be copied.
152
+ *
153
+ * 🛑 THROWS on failure, like `handleDelete` above and for the same reason:
154
+ * `OwnerQuickActions` catches and toasts, so returning quietly is
155
+ * indistinguishable from "it worked but the page did not move", and the
156
+ * obvious user response is to press it again. The previous product-specific
157
+ * implementation returned early after showing its own toast, which is the
158
+ * double-toast path this avoids.
159
+ */
160
+ const handleClone = useCallback(async () => {
161
+ if (!wiring.clone)
162
+ return;
163
+ const res = await fetch(wiring.clone(recordId), { method: "POST" });
164
+ if (!res.ok) {
165
+ const body = (await res.json().catch(() => null));
166
+ throw Object.assign(new Error("Clone failed"), { code: body?.code });
167
+ }
168
+ const json = (await res.json().catch(() => null));
169
+ const newId = json?.data?.id;
170
+ if (newId && wiring.afterClone && typeof window !== "undefined") {
171
+ window.location.assign(wiring.afterClone(newId));
172
+ }
173
+ }, [wiring, recordId]);
174
+ return (_jsx(OwnerQuickActions, { resource: resource, placement: placement, onMedia: onMedia, className: className, recordLabel: recordLabel, ownerId: ownerId, storeId: storeId, editHref: wiring.editHref(recordId), onDelete: handleDelete, onClone: wiring.clone ? handleClone : undefined }));
128
175
  }
@@ -9,6 +9,40 @@ export interface AdminStatCardProps {
9
9
  /** Named preset or raw CSS gradient string */
10
10
  gradient?: keyof typeof GRADIENTS | string;
11
11
  className?: string;
12
+ /**
13
+ * Change against the previous comparable period, as a SIGNED percentage.
14
+ * `12.4` renders "▲ 12.4%", `-3` renders "▼ 3%", `0` renders "no change".
15
+ *
16
+ * 🛑 OMIT IT WHEN THERE IS NO COMPARISON, and never pass `0` to mean "we
17
+ * did not measure". A number here is a factual claim about a previous
18
+ * period; `0` claims the figure held steady, which is a different and
19
+ * stronger statement than silence. The whole reason a trend is worth adding
20
+ * is that it is trusted, and a fabricated flat line is the cheapest way to
21
+ * lose that.
22
+ */
23
+ trendPercent?: number;
24
+ /**
25
+ * What `trendPercent` is measured against — "vs last week", "vs yesterday".
26
+ *
27
+ * 🛑 REQUIRED WHENEVER `trendPercent` IS PASSED, enforced by the discriminated
28
+ * shape below rather than by a comment. "▲ 12.4%" alone is unreadable: up
29
+ * on what? The same number is excellent against last month and alarming
30
+ * against last hour.
31
+ */
32
+ trendLabel?: string;
33
+ /**
34
+ * 🛑 DOES A HIGHER NUMBER MEAN BETTER? Defaults to `true`, and the exception
35
+ * is the reason this prop exists: refunds, cancellations, failed payments
36
+ * and error counts all go UP when things go WRONG. Colouring those green
37
+ * because the arrow points up is a dashboard actively misinforming the
38
+ * person reading it.
39
+ */
40
+ higherIsBetter?: boolean;
41
+ /**
42
+ * Recent values, oldest first, for the sparkline. Needs at least two points
43
+ * to draw a line; fewer renders nothing rather than a dot or a flat stub.
44
+ */
45
+ series?: readonly number[];
12
46
  }
13
- export declare function AdminStatCard({ label, value, sub, icon, gradient, className, }: AdminStatCardProps): React.JSX.Element;
47
+ export declare function AdminStatCard({ label, value, sub, icon, gradient, className, trendPercent, trendLabel, higherIsBetter, series, }: AdminStatCardProps): React.JSX.Element;
14
48
  export {};