@mohasinac/appkit 2.7.58 → 2.8.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 (364) hide show
  1. package/dist/_internal/client/features/filters/filter-load-options.d.ts +4 -5
  2. package/dist/_internal/server/features/auctions/og.js +8 -32
  3. package/dist/_internal/server/features/blog/og.js +10 -49
  4. package/dist/_internal/server/features/checkout/actions.js +6 -0
  5. package/dist/_internal/server/features/classified/og.js +9 -51
  6. package/dist/_internal/server/features/digital-code/actions.d.ts +11 -0
  7. package/dist/_internal/server/features/digital-code/actions.js +30 -0
  8. package/dist/_internal/server/features/digital-code/index.d.ts +1 -0
  9. package/dist/_internal/server/features/digital-code/index.js +1 -0
  10. package/dist/_internal/server/features/digital-code/og.js +9 -51
  11. package/dist/_internal/server/features/events/og.js +10 -49
  12. package/dist/_internal/server/features/item-requests/data.d.ts +17 -0
  13. package/dist/_internal/server/features/item-requests/data.js +20 -0
  14. package/dist/_internal/server/features/item-requests/index.d.ts +2 -0
  15. package/dist/_internal/server/features/item-requests/index.js +2 -0
  16. package/dist/_internal/server/features/item-requests/og.d.ts +40 -0
  17. package/dist/_internal/server/features/item-requests/og.js +136 -0
  18. package/dist/_internal/server/features/live/actions.d.ts +5 -0
  19. package/dist/_internal/server/features/live/actions.js +24 -0
  20. package/dist/_internal/server/features/live/index.d.ts +1 -0
  21. package/dist/_internal/server/features/live/index.js +1 -0
  22. package/dist/_internal/server/features/live/og.js +9 -51
  23. package/dist/_internal/server/features/media/contextGuards.js +19 -8
  24. package/dist/_internal/server/features/pre-orders/og.js +8 -32
  25. package/dist/_internal/server/features/prize-draws/data.d.ts +17 -0
  26. package/dist/_internal/server/features/prize-draws/data.js +20 -0
  27. package/dist/_internal/server/features/prize-draws/index.d.ts +2 -0
  28. package/dist/_internal/server/features/prize-draws/index.js +2 -0
  29. package/dist/_internal/server/features/prize-draws/og.d.ts +30 -0
  30. package/dist/_internal/server/features/prize-draws/og.js +50 -0
  31. package/dist/_internal/server/features/products/og.js +8 -33
  32. package/dist/_internal/server/features/scams/og.js +1 -1
  33. package/dist/_internal/server/features/seo/og-layout.d.ts +42 -0
  34. package/dist/_internal/server/features/seo/og-layout.js +69 -0
  35. package/dist/_internal/server/features/stores/og.js +8 -41
  36. package/dist/_internal/server/features/sublisting-categories/og.js +11 -42
  37. package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
  38. package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
  39. package/dist/_internal/shared/config/schema.d.ts +4 -4
  40. package/dist/client.d.ts +13 -6
  41. package/dist/client.js +9 -3
  42. package/dist/configs/next.js +11 -0
  43. package/dist/constants/index.d.ts +4 -0
  44. package/dist/constants/index.js +2 -0
  45. package/dist/constants/notification-types.d.ts +20 -0
  46. package/dist/constants/notification-types.js +37 -0
  47. package/dist/constants/pagination-presets.d.ts +39 -0
  48. package/dist/constants/pagination-presets.js +38 -0
  49. package/dist/errors/messages.d.ts +0 -4
  50. package/dist/errors/messages.js +0 -4
  51. package/dist/features/account/components/AddressesIndexListing.js +3 -4
  52. package/dist/features/account/components/UserOffersPanel.js +1 -1
  53. package/dist/features/account/components/UserSidebar.js +14 -10
  54. package/dist/features/account/components/UserSupportView.d.ts +1 -2
  55. package/dist/features/account/components/UserSupportView.js +9 -1
  56. package/dist/features/admin/actions/admin-actions.js +16 -1
  57. package/dist/features/admin/components/AdminAllEventEntriesView.d.ts +2 -3
  58. package/dist/features/admin/components/AdminAllEventEntriesView.js +50 -89
  59. package/dist/features/admin/components/AdminBidsView.d.ts +2 -3
  60. package/dist/features/admin/components/AdminBidsView.js +64 -104
  61. package/dist/features/admin/components/AdminBlogView.d.ts +3 -7
  62. package/dist/features/admin/components/AdminBlogView.js +53 -88
  63. package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
  64. package/dist/features/admin/components/AdminBrandsView.d.ts +2 -3
  65. package/dist/features/admin/components/AdminBrandsView.js +85 -89
  66. package/dist/features/admin/components/AdminBundlesView.js +68 -130
  67. package/dist/features/admin/components/AdminCarouselView.d.ts +2 -2
  68. package/dist/features/admin/components/AdminCarouselView.js +22 -70
  69. package/dist/features/admin/components/AdminCartsView.d.ts +2 -3
  70. package/dist/features/admin/components/AdminCartsView.js +39 -89
  71. package/dist/features/admin/components/AdminCategoriesView.d.ts +3 -3
  72. package/dist/features/admin/components/AdminCategoriesView.js +97 -96
  73. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -6
  74. package/dist/features/admin/components/AdminClassifiedView.d.ts +3 -0
  75. package/dist/features/admin/components/AdminClassifiedView.js +44 -0
  76. package/dist/features/admin/components/AdminContactView.d.ts +2 -2
  77. package/dist/features/admin/components/AdminContactView.js +105 -130
  78. package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
  79. package/dist/features/admin/components/AdminCouponsView.d.ts +3 -6
  80. package/dist/features/admin/components/AdminCouponsView.js +69 -97
  81. package/dist/features/admin/components/AdminDigitalCodesView.d.ts +3 -0
  82. package/dist/features/admin/components/AdminDigitalCodesView.js +51 -0
  83. package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
  84. package/dist/features/admin/components/AdminFaqsView.d.ts +4 -4
  85. package/dist/features/admin/components/AdminFaqsView.js +79 -86
  86. package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
  87. package/dist/features/admin/components/AdminFeaturesView.d.ts +2 -3
  88. package/dist/features/admin/components/AdminFeaturesView.js +57 -70
  89. package/dist/features/admin/components/AdminGroupedListingsView.d.ts +3 -0
  90. package/dist/features/admin/components/AdminGroupedListingsView.js +43 -0
  91. package/dist/features/admin/components/AdminHistoryView.d.ts +2 -3
  92. package/dist/features/admin/components/AdminHistoryView.js +35 -52
  93. package/dist/features/admin/components/AdminListingScaffold.d.ts +2 -2
  94. package/dist/features/admin/components/AdminListingScaffold.js +3 -3
  95. package/dist/features/admin/components/AdminLiveView.d.ts +3 -0
  96. package/dist/features/admin/components/AdminLiveView.js +50 -0
  97. package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -2
  98. package/dist/features/admin/components/AdminNewsletterView.js +68 -108
  99. package/dist/features/admin/components/AdminNotificationsView.d.ts +2 -3
  100. package/dist/features/admin/components/AdminNotificationsView.js +67 -99
  101. package/dist/features/admin/components/AdminOrderEditorView.js +10 -10
  102. package/dist/features/admin/components/AdminOrdersView.d.ts +2 -3
  103. package/dist/features/admin/components/AdminOrdersView.js +93 -113
  104. package/dist/features/admin/components/AdminPayoutsView.d.ts +2 -3
  105. package/dist/features/admin/components/AdminPayoutsView.js +51 -97
  106. package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +2 -3
  107. package/dist/features/admin/components/AdminPrizeDrawsView.js +41 -83
  108. package/dist/features/admin/components/AdminProductEditorView.js +3 -7
  109. package/dist/features/admin/components/AdminProductsView.d.ts +4 -4
  110. package/dist/features/admin/components/AdminProductsView.js +166 -167
  111. package/dist/features/admin/components/AdminReturnRequestsView.d.ts +1 -1
  112. package/dist/features/admin/components/AdminReturnRequestsView.js +64 -66
  113. package/dist/features/admin/components/AdminReviewsView.d.ts +3 -7
  114. package/dist/features/admin/components/AdminReviewsView.js +94 -116
  115. package/dist/features/admin/components/AdminScammersView.d.ts +2 -3
  116. package/dist/features/admin/components/AdminScammersView.js +53 -103
  117. package/dist/features/admin/components/AdminSectionsView.js +1 -1
  118. package/dist/features/admin/components/AdminSessionsView.d.ts +2 -3
  119. package/dist/features/admin/components/AdminSessionsView.js +62 -95
  120. package/dist/features/admin/components/AdminStoreAddressesView.d.ts +1 -1
  121. package/dist/features/admin/components/AdminStoreAddressesView.js +39 -53
  122. package/dist/features/admin/components/AdminStoreEditorView.js +21 -8
  123. package/dist/features/admin/components/AdminStoresView.d.ts +2 -3
  124. package/dist/features/admin/components/AdminStoresView.js +67 -105
  125. package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +1 -1
  126. package/dist/features/admin/components/AdminSublistingCategoriesView.js +29 -49
  127. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
  128. package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +10 -1
  129. package/dist/features/admin/components/AdminSupportTicketDetailView.js +191 -5
  130. package/dist/features/admin/components/AdminSupportTicketsView.d.ts +2 -3
  131. package/dist/features/admin/components/AdminSupportTicketsView.js +65 -102
  132. package/dist/features/admin/components/AdminTeamView.d.ts +2 -2
  133. package/dist/features/admin/components/AdminTeamView.js +42 -85
  134. package/dist/features/admin/components/AdminUserEditorView.d.ts +11 -1
  135. package/dist/features/admin/components/AdminUserEditorView.js +80 -6
  136. package/dist/features/admin/components/AdminUsersView.d.ts +2 -3
  137. package/dist/features/admin/components/AdminUsersView.js +108 -106
  138. package/dist/features/admin/components/AdminViewCards.d.ts +1 -1
  139. package/dist/features/admin/components/AdminWishlistsView.d.ts +2 -3
  140. package/dist/features/admin/components/AdminWishlistsView.js +36 -62
  141. package/dist/features/admin/components/DataListingView.d.ts +122 -0
  142. package/dist/features/admin/components/DataListingView.js +66 -0
  143. package/dist/features/admin/components/index.d.ts +10 -4
  144. package/dist/features/admin/components/index.js +6 -2
  145. package/dist/features/admin/constants/filter-tabs.d.ts +23 -17
  146. package/dist/features/admin/constants/filter-tabs.js +21 -17
  147. package/dist/features/admin/hooks/useAdminListing.d.ts +59 -0
  148. package/dist/features/admin/hooks/useAdminListing.js +71 -0
  149. package/dist/features/admin/schemas/firestore.js +6 -7
  150. package/dist/features/auctions/server.d.ts +0 -1
  151. package/dist/features/auctions/server.js +0 -1
  152. package/dist/features/auth/actions/realtime-token-actions.js +1 -1
  153. package/dist/features/auth/repository/user.repository.js +1 -1
  154. package/dist/features/auth/schemas/firestore.d.ts +1 -0
  155. package/dist/features/blog/components/BlogIndexListing.js +3 -6
  156. package/dist/features/cart/repository/cart.repository.d.ts +4 -1
  157. package/dist/features/cart/repository/cart.repository.js +12 -0
  158. package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
  159. package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -2
  160. package/dist/features/checkout/index.d.ts +1 -1
  161. package/dist/features/events/components/AdminEventsView.d.ts +3 -3
  162. package/dist/features/events/components/AdminEventsView.js +50 -86
  163. package/dist/features/events/components/EventOfferCard.d.ts +19 -0
  164. package/dist/features/events/components/EventOfferCard.js +29 -0
  165. package/dist/features/events/components/EventRaffleEntryForm.d.ts +25 -0
  166. package/dist/features/events/components/EventRaffleEntryForm.js +37 -0
  167. package/dist/features/events/components/EventRafflesSection.d.ts +1 -4
  168. package/dist/features/events/components/EventRafflesSection.js +17 -6
  169. package/dist/features/events/components/EventSaleBanner.d.ts +21 -0
  170. package/dist/features/events/components/EventSaleBanner.js +18 -0
  171. package/dist/features/events/components/EventsIndexListing.js +3 -4
  172. package/dist/features/events/components/index.d.ts +6 -0
  173. package/dist/features/events/components/index.js +4 -0
  174. package/dist/features/events/repository/event-entry.repository.d.ts +5 -0
  175. package/dist/features/events/repository/event-entry.repository.js +7 -0
  176. package/dist/features/filters/AsyncFacetSection.d.ts +1 -1
  177. package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
  178. package/dist/features/homepage/components/AnnouncementBar.js +51 -4
  179. package/dist/features/homepage/components/FAQSection.d.ts +0 -5
  180. package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
  181. package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
  182. package/dist/features/homepage/components/index.d.ts +1 -1
  183. package/dist/features/homepage/components/index.js +1 -1
  184. package/dist/features/homepage/lib/live-stats.js +1 -1
  185. package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
  186. package/dist/features/homepage/lib/section-renderer.js +2 -2
  187. package/dist/features/homepage/schemas/firestore.d.ts +0 -2
  188. package/dist/features/layout/index.d.ts +0 -2
  189. package/dist/features/layout/index.js +0 -1
  190. package/dist/features/orders/repository/orders.repository.d.ts +0 -1
  191. package/dist/features/orders/repository/orders.repository.js +0 -1
  192. package/dist/features/orders/server.d.ts +1 -1
  193. package/dist/features/orders/server.js +1 -1
  194. package/dist/features/pre-orders/server.d.ts +0 -1
  195. package/dist/features/pre-orders/server.js +0 -1
  196. package/dist/features/products/components/GroupSettingsPanel.js +65 -49
  197. package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
  198. package/dist/features/products/components/PrizeDrawsSection.d.ts +1 -4
  199. package/dist/features/products/components/PrizeDrawsSection.js +17 -6
  200. package/dist/features/products/components/PrizeRevealModal.js +1 -1
  201. package/dist/features/products/components/ProductCardMetadataSection.d.ts +11 -0
  202. package/dist/features/products/components/ProductCardMetadataSection.js +25 -0
  203. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  204. package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
  205. package/dist/features/products/components/ProductGradingTab.d.ts +13 -0
  206. package/dist/features/products/components/ProductGradingTab.js +25 -0
  207. package/dist/features/products/components/ProductGrid.js +14 -2
  208. package/dist/features/products/components/ProductTabsShell.d.ts +5 -1
  209. package/dist/features/products/components/ProductTabsShell.js +6 -1
  210. package/dist/features/products/components/index.d.ts +4 -0
  211. package/dist/features/products/components/index.js +3 -0
  212. package/dist/features/products/constants/action-defs.d.ts +5 -0
  213. package/dist/features/products/constants/action-defs.js +52 -32
  214. package/dist/features/products/constants/sieve.d.ts +24 -4
  215. package/dist/features/products/constants/sieve.js +20 -11
  216. package/dist/features/products/repository/products.repository.d.ts +0 -2
  217. package/dist/features/products/repository/products.repository.js +0 -2
  218. package/dist/features/products/schemas/firestore.d.ts +17 -0
  219. package/dist/features/products/schemas/product-features.validators.d.ts +2 -2
  220. package/dist/features/products/schemas/product-templates.d.ts +2 -4
  221. package/dist/features/products/server.d.ts +1 -1
  222. package/dist/features/products/server.js +1 -1
  223. package/dist/features/products/types/index.d.ts +4 -0
  224. package/dist/features/promotions/components/CouponCard.js +5 -5
  225. package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
  226. package/dist/features/promotions/server.d.ts +0 -1
  227. package/dist/features/promotions/server.js +0 -1
  228. package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
  229. package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
  230. package/dist/features/reviews/repository/reviews.repository.js +0 -1
  231. package/dist/features/reviews/server.d.ts +1 -1
  232. package/dist/features/reviews/server.js +1 -1
  233. package/dist/features/search/api/route.js +44 -1
  234. package/dist/features/search/components/Search.js +2 -0
  235. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  236. package/dist/features/search/hooks/useNavSuggestions.js +31 -4
  237. package/dist/features/seller/components/BrandInlineSelect.js +2 -2
  238. package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
  239. package/dist/features/seller/components/SellerAddressesView.js +10 -20
  240. package/dist/features/seller/components/SellerAuctionsView.d.ts +3 -3
  241. package/dist/features/seller/components/SellerAuctionsView.js +67 -106
  242. package/dist/features/seller/components/SellerBidsView.js +6 -6
  243. package/dist/features/seller/components/SellerBundlesView.js +72 -105
  244. package/dist/features/seller/components/SellerClassifiedView.js +74 -105
  245. package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
  246. package/dist/features/seller/components/SellerCouponsView.d.ts +2 -2
  247. package/dist/features/seller/components/SellerCouponsView.js +51 -96
  248. package/dist/features/seller/components/SellerDigitalCodesView.js +70 -103
  249. package/dist/features/seller/components/SellerGroupedListingsView.js +34 -54
  250. package/dist/features/seller/components/SellerLiveView.js +70 -99
  251. package/dist/features/seller/components/SellerOffersPanel.js +1 -1
  252. package/dist/features/seller/components/SellerOffersView.d.ts +2 -2
  253. package/dist/features/seller/components/SellerOffersView.js +37 -70
  254. package/dist/features/seller/components/SellerOrdersView.d.ts +2 -2
  255. package/dist/features/seller/components/SellerOrdersView.js +10 -24
  256. package/dist/features/seller/components/SellerPayoutMethodsView.js +12 -24
  257. package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -2
  258. package/dist/features/seller/components/SellerPayoutsView.js +34 -69
  259. package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -2
  260. package/dist/features/seller/components/SellerPreOrdersView.js +55 -97
  261. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -2
  262. package/dist/features/seller/components/SellerPrizeDrawsView.js +55 -97
  263. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  264. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
  265. package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
  266. package/dist/features/seller/components/SellerProductsView.d.ts +2 -2
  267. package/dist/features/seller/components/SellerProductsView.js +24 -32
  268. package/dist/features/seller/components/SellerShippingConfigsView.js +12 -24
  269. package/dist/features/seller/components/SellerSidebar.d.ts +0 -4
  270. package/dist/features/seller/components/SellerSidebar.js +0 -2
  271. package/dist/features/seller/components/SellerStoreCategoriesView.js +15 -34
  272. package/dist/features/seller/components/SellerTemplatesView.js +27 -43
  273. package/dist/features/seller/components/SellersListView.d.ts +1 -2
  274. package/dist/features/seller/components/analytics/SellerAnalyticsStats.d.ts +5 -0
  275. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +2 -8
  276. package/dist/features/seller/components/index.d.ts +0 -3
  277. package/dist/features/seller/components/index.js +0 -2
  278. package/dist/features/seller/server.d.ts +0 -1
  279. package/dist/features/seller/server.js +0 -1
  280. package/dist/features/shell/FormShell.d.ts +1 -1
  281. package/dist/features/shell/FormShell.js +4 -1
  282. package/dist/features/shell/field-groups/ImageFieldGroup.d.ts +18 -0
  283. package/dist/features/shell/field-groups/ImageFieldGroup.js +15 -0
  284. package/dist/features/shell/field-groups/SeoFieldGroup.d.ts +22 -0
  285. package/dist/features/shell/field-groups/SeoFieldGroup.js +16 -0
  286. package/dist/features/shell/field-groups/StatusFieldGroup.d.ts +35 -0
  287. package/dist/features/shell/field-groups/StatusFieldGroup.js +16 -0
  288. package/dist/features/shell/field-groups/TitleDescriptionGroup.d.ts +40 -0
  289. package/dist/features/shell/field-groups/TitleDescriptionGroup.js +16 -0
  290. package/dist/features/shell/field-groups/index.d.ts +13 -0
  291. package/dist/features/shell/field-groups/index.js +9 -0
  292. package/dist/features/shell/index.d.ts +2 -0
  293. package/dist/features/shell/index.js +1 -0
  294. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +1 -2
  295. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +1 -2
  296. package/dist/features/stores/components/StoreHeader.js +2 -1
  297. package/dist/features/stores/components/StoreListingsGuideView.d.ts +1 -2
  298. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +1 -2
  299. package/dist/features/stores/components/StoreReviewsListing.js +3 -4
  300. package/dist/features/stores/components/StoreScopedSearch.d.ts +16 -0
  301. package/dist/features/stores/components/StoreScopedSearch.js +27 -0
  302. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +1 -2
  303. package/dist/features/stores/components/StoresIndexListing.js +3 -4
  304. package/dist/features/stores/server.d.ts +0 -1
  305. package/dist/features/stores/server.js +0 -1
  306. package/dist/features/support/schemas/firestore.d.ts +22 -1
  307. package/dist/features/support/schemas/firestore.js +10 -0
  308. package/dist/features/wishlist/server.d.ts +0 -1
  309. package/dist/features/wishlist/server.js +0 -1
  310. package/dist/index.d.ts +11 -33
  311. package/dist/index.js +17 -62
  312. package/dist/next/routing/route-map.d.ts +8 -0
  313. package/dist/next/routing/route-map.js +4 -0
  314. package/dist/react/contexts/SessionContext.d.ts +2 -0
  315. package/dist/react/contexts/SessionContext.js +2 -0
  316. package/dist/react/hooks/useEntityDelete.d.ts +15 -0
  317. package/dist/react/hooks/useEntityDelete.js +39 -0
  318. package/dist/react/hooks/useListingTypeFlags.d.ts +32 -0
  319. package/dist/react/hooks/useListingTypeFlags.js +19 -0
  320. package/dist/react/index.d.ts +2 -0
  321. package/dist/react/index.js +2 -0
  322. package/dist/repositories/index.d.ts +3 -3
  323. package/dist/repositories/index.js +3 -3
  324. package/dist/security/index.d.ts +1 -1
  325. package/dist/security/index.js +1 -1
  326. package/dist/security/pii-redact.d.ts +8 -0
  327. package/dist/security/pii-redact.js +10 -1
  328. package/dist/seed/grouped-listings-seed-data.d.ts +4 -0
  329. package/dist/seed/products-preorders-seed-data.d.ts +4 -0
  330. package/dist/seed/products-prize-draws-seed-data.js +3 -0
  331. package/dist/seed/site-settings-seed-data.js +5 -5
  332. package/dist/seed/sub-listings-seed-data.d.ts +4 -0
  333. package/dist/server.d.ts +2 -0
  334. package/dist/server.js +4 -0
  335. package/dist/styles.css +54 -395
  336. package/dist/tailwind-utilities.css +1 -1
  337. package/dist/ui/components/BaseListingCard.d.ts +10 -0
  338. package/dist/ui/components/BaseListingCard.js +17 -1
  339. package/dist/ui/components/DetailPageGallery.d.ts +32 -0
  340. package/dist/ui/components/DetailPageGallery.js +47 -0
  341. package/dist/ui/components/DetailPageHero.d.ts +30 -0
  342. package/dist/ui/components/DetailPageHero.js +22 -0
  343. package/dist/ui/components/DetailPageTabs.d.ts +34 -0
  344. package/dist/ui/components/DetailPageTabs.js +38 -0
  345. package/dist/ui/components/HorizontalScroller.style.css +15 -2
  346. package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
  347. package/dist/ui/components/ListingFilterDrawer.js +8 -0
  348. package/dist/ui/components/ListingLayout.d.ts +11 -2
  349. package/dist/ui/components/ListingLayout.js +12 -6
  350. package/dist/ui/components/PaginatedSelect.d.ts +66 -0
  351. package/dist/ui/components/PaginatedSelect.js +201 -0
  352. package/dist/ui/components/PaginatedSelect.style.css +254 -0
  353. package/dist/ui/components/Semantic.d.ts +0 -4
  354. package/dist/ui/components/Semantic.js +0 -2
  355. package/dist/ui/components/SettingsSection.d.ts +47 -0
  356. package/dist/ui/components/SettingsSection.js +38 -0
  357. package/dist/ui/components/index.style.css +1 -4
  358. package/dist/ui/index.d.ts +14 -14
  359. package/dist/ui/index.js +7 -7
  360. package/dist/utils/id-generators.d.ts +1 -18
  361. package/dist/utils/id-generators.js +51 -18
  362. package/dist/validation/schemas.d.ts +25 -0
  363. package/dist/validation/schemas.js +28 -0
  364. package/package.json +1 -1
@@ -4,17 +4,22 @@ import { useState } from "react";
4
4
  const STATIC_TABS = [
5
5
  { id: "description", label: "Description" },
6
6
  { id: "specs", label: "Specifications" },
7
+ { id: "grading", label: "Grading" },
7
8
  { id: "ingredients", label: "Ingredients" },
8
9
  { id: "howToUse", label: "How to Use" },
9
10
  { id: "reviews", label: "Reviews" },
11
+ // W1-34 — Q&A tab; consumers pass `qaContent` to surface it.
12
+ { id: "qa", label: "Q&A" },
10
13
  ];
11
- export function ProductTabsShell({ descriptionContent, specsContent, ingredientsContent, howToUseContent, reviewsContent, customTabs = [], className = "", }) {
14
+ export function ProductTabsShell({ descriptionContent, specsContent, gradingContent, ingredientsContent, howToUseContent, reviewsContent, qaContent, customTabs = [], className = "", }) {
12
15
  const staticContentMap = {
13
16
  description: descriptionContent,
14
17
  specs: specsContent,
18
+ grading: gradingContent,
15
19
  ingredients: ingredientsContent,
16
20
  howToUse: howToUseContent,
17
21
  reviews: reviewsContent,
22
+ qa: qaContent,
18
23
  };
19
24
  const visibleStatic = STATIC_TABS.filter((t) => staticContentMap[t.id] != null);
20
25
  const visibleCustom = customTabs.filter((t) => t.content != null);
@@ -54,6 +54,10 @@ export { ProductGalleryClient } from "./ProductGalleryClient";
54
54
  export type { ProductGalleryClientProps } from "./ProductGalleryClient";
55
55
  export { ProductTabsShell } from "./ProductTabsShell";
56
56
  export type { ProductTabsShellProps, CustomTabDef } from "./ProductTabsShell";
57
+ export { ProductGradingTab } from "./ProductGradingTab";
58
+ export type { ProductGradingTabProps } from "./ProductGradingTab";
59
+ export { ProductCardMetadataSection } from "./ProductCardMetadataSection";
60
+ export type { ProductCardMetadataSectionProps } from "./ProductCardMetadataSection";
57
61
  export { CustomFieldsEditor } from "./CustomFieldsEditor";
58
62
  export type { CustomFieldsEditorProps } from "./CustomFieldsEditor";
59
63
  export { CustomSectionsEditor } from "./CustomSectionsEditor";
@@ -32,6 +32,9 @@ export { MakeOfferButton } from "./MakeOfferButton";
32
32
  export { RelatedProducts } from "./RelatedProducts";
33
33
  export { ProductGalleryClient } from "./ProductGalleryClient";
34
34
  export { ProductTabsShell } from "./ProductTabsShell";
35
+ // W1-34: Product detail missing sections
36
+ export { ProductGradingTab } from "./ProductGradingTab";
37
+ export { ProductCardMetadataSection } from "./ProductCardMetadataSection";
35
38
  export { CustomFieldsEditor } from "./CustomFieldsEditor";
36
39
  export { CustomSectionsEditor } from "./CustomSectionsEditor";
37
40
  export { CustomSectionTabContent } from "./CustomSectionTabContent";
@@ -9,6 +9,11 @@
9
9
  * Client — useAuthGate() shows LoginRequiredModal for Tier 1 (public) actions
10
10
  * Server — checkActionAllowed() enforces the same guards on every server action
11
11
  * Admin — ActionPermissionsManager toggles defaultEnabled via siteSettings.actionConfig
12
+ *
13
+ * Label source of truth: `ACTIONS.{RESOURCE}["..."].label` in
14
+ * _internal/shared/actions/action-registry.ts. `ACTION_META` derives its labels
15
+ * via `regLabel()` where a registry counterpart exists; entries with no
16
+ * counterpart keep an inline literal until a registry leaf is added (W1-5).
12
17
  */
13
18
  export type ActionVariant = "primary" | "secondary" | "outline" | "ghost" | "danger" | "warning";
14
19
  export declare const ACTION_ID: {
@@ -9,7 +9,26 @@
9
9
  * Client — useAuthGate() shows LoginRequiredModal for Tier 1 (public) actions
10
10
  * Server — checkActionAllowed() enforces the same guards on every server action
11
11
  * Admin — ActionPermissionsManager toggles defaultEnabled via siteSettings.actionConfig
12
+ *
13
+ * Label source of truth: `ACTIONS.{RESOURCE}["..."].label` in
14
+ * _internal/shared/actions/action-registry.ts. `ACTION_META` derives its labels
15
+ * via `regLabel()` where a registry counterpart exists; entries with no
16
+ * counterpart keep an inline literal until a registry leaf is added (W1-5).
17
+ */
18
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ // Label-from-registry helper (W1-5)
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+ /**
23
+ * Returns the label for `ACTIONS[resource][id]`. The `fallback` is used only
24
+ * when the registry has no leaf yet — orphan IDs should be added to ACTIONS
25
+ * over time. Adding the fallback parameter as required ensures every call
26
+ * site documents what the inline label was at migration time, simplifying
27
+ * the eventual cleanup.
12
28
  */
29
+ function regLabel(resource, id, fallback) {
30
+ return ACTIONS[resource]?.[id]?.label ?? fallback;
31
+ }
13
32
  // ─────────────────────────────────────────────────────────────────────────────
14
33
  // § 1 Product / listing actions
15
34
  // Used by: detail pages, BulkActionsBar, BuyBar
@@ -64,49 +83,50 @@ export const ACTION_ID = {
64
83
  DELETE_ACCOUNT: "delete-account",
65
84
  };
66
85
  export const ACTION_META = {
67
- [ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: "Buy Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
68
- [ACTION_ID.ADD_TO_CART]: { id: ACTION_ID.ADD_TO_CART, label: "Add to Cart", variant: "secondary", iconName: "ShoppingCart" },
69
- [ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: "Add to Wishlist", variant: "ghost", iconName: "Heart", requiresAuth: true, authMessage: "You need to be signed in to save items to your wishlist." },
70
- [ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: "Remove from Wishlist", variant: "ghost", iconName: "HeartOff", requiresAuth: true, authMessage: "You need to be signed in to manage your wishlist." },
71
- [ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: "Make an Offer", variant: "outline", iconName: "Tag", requiresAuth: true, authMessage: "You need to be signed in to make an offer." },
72
- [ACTION_ID.SHARE]: { id: ACTION_ID.SHARE, label: "Share", variant: "ghost", iconName: "Share2" },
73
- [ACTION_ID.COMPARE]: { id: ACTION_ID.COMPARE, label: "Compare", variant: "secondary", iconName: "Columns" },
74
- [ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: "Place Bid", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to place a bid." },
75
- [ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: "Buy Now", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
76
- [ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: "Watch", variant: "ghost", iconName: "Eye", requiresAuth: true, authMessage: "You need to be signed in to watch auctions." },
77
- [ACTION_ID.UNWATCH_AUCTION]: { id: ACTION_ID.UNWATCH_AUCTION, label: "Unwatch", variant: "ghost", iconName: "EyeOff", requiresAuth: true, authMessage: "You need to be signed in to manage your watchlist." },
78
- [ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: "Reserve Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to reserve a pre-order." },
79
- [ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: "Cancel Reservation", variant: "danger", iconName: "X", requiresAuth: true, authMessage: "You need to be signed in to cancel a reservation." },
86
+ [ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: regLabel("PRODUCT", "buy-now", "Buy Now"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
87
+ [ACTION_ID.ADD_TO_CART]: { id: ACTION_ID.ADD_TO_CART, label: regLabel("PRODUCT", "add-to-cart", "Add to Cart"), variant: "secondary", iconName: "ShoppingCart" },
88
+ [ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: regLabel("PRODUCT", "add-to-wishlist", "Add to Wishlist"), variant: "ghost", iconName: "Heart", requiresAuth: true, authMessage: "You need to be signed in to save items to your wishlist." },
89
+ [ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: regLabel("PRODUCT", "remove-from-wishlist", "Remove from Wishlist"), variant: "ghost", iconName: "HeartOff", requiresAuth: true, authMessage: "You need to be signed in to manage your wishlist." },
90
+ [ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: regLabel("PRODUCT", "make-offer", "Make an Offer"), variant: "outline", iconName: "Tag", requiresAuth: true, authMessage: "You need to be signed in to make an offer." },
91
+ [ACTION_ID.SHARE]: { id: ACTION_ID.SHARE, label: regLabel("PRODUCT", "share", "Share"), variant: "ghost", iconName: "Share2" },
92
+ [ACTION_ID.COMPARE]: { id: ACTION_ID.COMPARE, label: regLabel("PRODUCT", "compare", "Compare"), variant: "secondary", iconName: "Columns" },
93
+ [ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: regLabel("AUCTION", "place-bid", "Place Bid"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to place a bid." },
94
+ [ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: regLabel("AUCTION", "buy-it-now", "Buy Now"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
95
+ [ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: regLabel("AUCTION", "watch", "Watch"), variant: "ghost", iconName: "Eye", requiresAuth: true, authMessage: "You need to be signed in to watch auctions." },
96
+ [ACTION_ID.UNWATCH_AUCTION]: { id: ACTION_ID.UNWATCH_AUCTION, label: regLabel("AUCTION", "unwatch", "Unwatch"), variant: "ghost", iconName: "EyeOff", requiresAuth: true, authMessage: "You need to be signed in to manage your watchlist." },
97
+ [ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: regLabel("PRE_ORDER", "reserve-now", "Reserve Now"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to reserve a pre-order." },
98
+ [ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: regLabel("PRE_ORDER", "cancel-reservation", "Cancel Reservation"), variant: "danger", iconName: "X", requiresAuth: true, authMessage: "You need to be signed in to cancel a reservation." },
80
99
  // ── Checkout / navigation CTAs ────────────────────────────────────────────
81
- [ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: "Checkout", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
82
- [ACTION_ID.CONTINUE_TO_VERIFY]: { id: ACTION_ID.CONTINUE_TO_VERIFY, label: "Continue", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
83
- [ACTION_ID.SEND_OTP]: { id: ACTION_ID.SEND_OTP, label: "Send Code", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
84
- [ACTION_ID.VERIFY_OTP]: { id: ACTION_ID.VERIFY_OTP, label: "Verify & Continue", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
85
- [ACTION_ID.PAY_ONLINE]: { id: ACTION_ID.PAY_ONLINE, label: "Pay Online", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
86
- [ACTION_ID.PAY_COD]: { id: ACTION_ID.PAY_COD, label: "Cash on Delivery", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
87
- [ACTION_ID.APPLY_COUPON]: { id: ACTION_ID.APPLY_COUPON, label: "Apply", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to apply a coupon." },
88
- [ACTION_ID.REMOVE_COUPON]: { id: ACTION_ID.REMOVE_COUPON, label: "Remove", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to manage coupons." },
89
- [ACTION_ID.ENTER_PRIZE_DRAW]: { id: ACTION_ID.ENTER_PRIZE_DRAW, label: "Enter Draw", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a prize draw." },
100
+ [ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: regLabel("CART", "checkout", "Checkout"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
101
+ [ACTION_ID.CONTINUE_TO_VERIFY]: { id: ACTION_ID.CONTINUE_TO_VERIFY, label: regLabel("CHECKOUT", "continue-to-verification", "Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
102
+ [ACTION_ID.SEND_OTP]: { id: ACTION_ID.SEND_OTP, label: regLabel("CHECKOUT", "send-otp", "Send Code"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
103
+ [ACTION_ID.VERIFY_OTP]: { id: ACTION_ID.VERIFY_OTP, label: regLabel("CHECKOUT", "verify-otp", "Verify & Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
104
+ [ACTION_ID.PAY_ONLINE]: { id: ACTION_ID.PAY_ONLINE, label: regLabel("CHECKOUT", "pay-online", "Pay Online"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
105
+ [ACTION_ID.PAY_COD]: { id: ACTION_ID.PAY_COD, label: regLabel("CHECKOUT", "pay-cod", "Cash on Delivery"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
106
+ [ACTION_ID.APPLY_COUPON]: { id: ACTION_ID.APPLY_COUPON, label: regLabel("CHECKOUT", "apply-coupon", "Apply"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to apply a coupon." },
107
+ [ACTION_ID.REMOVE_COUPON]: { id: ACTION_ID.REMOVE_COUPON, label: regLabel("CHECKOUT", "remove-coupon", "Remove"), variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to manage coupons." },
108
+ [ACTION_ID.ENTER_PRIZE_DRAW]: { id: ACTION_ID.ENTER_PRIZE_DRAW, label: regLabel("PRIZE_DRAW", "enter-draw", "Enter Draw"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a prize draw." },
109
+ // ── Inline literals: no registry counterpart yet (orphan IDs — add to ACTIONS in a follow-up sweep) ─
90
110
  [ACTION_ID.ENTER_RAFFLE]: { id: ACTION_ID.ENTER_RAFFLE, label: "Enter Raffle", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a raffle." },
91
111
  [ACTION_ID.REPORT_LISTING]: { id: ACTION_ID.REPORT_LISTING, label: "Report Listing", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to report a listing." },
92
- [ACTION_ID.FOLLOW_STORE]: { id: ACTION_ID.FOLLOW_STORE, label: "Follow Store", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to follow a store." },
112
+ [ACTION_ID.FOLLOW_STORE]: { id: ACTION_ID.FOLLOW_STORE, label: regLabel("STORE", "follow", "Follow Store"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to follow a store." },
93
113
  // ── Reviews & social ──────────────────────────────────────────────────────
94
- [ACTION_ID.WRITE_REVIEW]: { id: ACTION_ID.WRITE_REVIEW, label: "Write a Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to write a review." },
114
+ [ACTION_ID.WRITE_REVIEW]: { id: ACTION_ID.WRITE_REVIEW, label: regLabel("USER", "write-review", "Write a Review"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to write a review." },
95
115
  [ACTION_ID.MESSAGE_SELLER]: { id: ACTION_ID.MESSAGE_SELLER, label: "Message Seller", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to message a seller." },
96
116
  // ── Seller CTAs ───────────────────────────────────────────────────────────
97
117
  [ACTION_ID.BECOME_SELLER]: { id: ACTION_ID.BECOME_SELLER, label: "Apply as Seller", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to apply as a seller." },
98
- [ACTION_ID.REQUEST_PAYOUT]: { id: ACTION_ID.REQUEST_PAYOUT, label: "Request Payout", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a payout.", requiredPermission: "seller.payouts.request" },
99
- [ACTION_ID.RESPOND_TO_REVIEW]: { id: ACTION_ID.RESPOND_TO_REVIEW, label: "Respond to Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to respond to a review.", requiredPermission: "seller.reviews.respond" },
118
+ [ACTION_ID.REQUEST_PAYOUT]: { id: ACTION_ID.REQUEST_PAYOUT, label: regLabel("STORE", "request-payout", "Request Payout"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a payout.", requiredPermission: "seller.payouts.request" },
119
+ [ACTION_ID.RESPOND_TO_REVIEW]: { id: ACTION_ID.RESPOND_TO_REVIEW, label: regLabel("STORE", "reply-review", "Respond to Review"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to respond to a review.", requiredPermission: "seller.reviews.respond" },
100
120
  // ── User account actions ──────────────────────────────────────────────────
101
- [ACTION_ID.CANCEL_ORDER]: { id: ACTION_ID.CANCEL_ORDER, label: "Cancel Order", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to cancel an order." },
102
- [ACTION_ID.REQUEST_RETURN]: { id: ACTION_ID.REQUEST_RETURN, label: "Request Return", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a return." },
103
- [ACTION_ID.REORDER]: { id: ACTION_ID.REORDER, label: "Reorder", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to reorder." },
104
- [ACTION_ID.TRACK_ORDER]: { id: ACTION_ID.TRACK_ORDER, label: "Track Order", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to track your order." },
121
+ [ACTION_ID.CANCEL_ORDER]: { id: ACTION_ID.CANCEL_ORDER, label: regLabel("USER", "cancel-order", "Cancel Order"), variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to cancel an order." },
122
+ [ACTION_ID.REQUEST_RETURN]: { id: ACTION_ID.REQUEST_RETURN, label: regLabel("USER", "request-return", "Request Return"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a return." },
123
+ [ACTION_ID.REORDER]: { id: ACTION_ID.REORDER, label: regLabel("USER", "reorder", "Reorder"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to reorder." },
124
+ [ACTION_ID.TRACK_ORDER]: { id: ACTION_ID.TRACK_ORDER, label: regLabel("USER", "track-order", "Track Order"), variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to track your order." },
105
125
  [ACTION_ID.EDIT_PROFILE]: { id: ACTION_ID.EDIT_PROFILE, label: "Edit Profile", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to edit your profile." },
106
126
  [ACTION_ID.ADD_ADDRESS]: { id: ACTION_ID.ADD_ADDRESS, label: "Add Address", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to add an address." },
107
127
  [ACTION_ID.EDIT_ADDRESS]: { id: ACTION_ID.EDIT_ADDRESS, label: "Edit Address", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to edit an address." },
108
- [ACTION_ID.DELETE_ADDRESS]: { id: ACTION_ID.DELETE_ADDRESS, label: "Delete Address", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete an address." },
109
- [ACTION_ID.CHANGE_PASSWORD]: { id: ACTION_ID.CHANGE_PASSWORD, label: "Change Password", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to change your password." },
128
+ [ACTION_ID.DELETE_ADDRESS]: { id: ACTION_ID.DELETE_ADDRESS, label: regLabel("USER", "delete-address", "Delete Address"), variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete an address." },
129
+ [ACTION_ID.CHANGE_PASSWORD]: { id: ACTION_ID.CHANGE_PASSWORD, label: regLabel("USER", "update-password", "Change Password"), variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to change your password." },
110
130
  [ACTION_ID.DELETE_ACCOUNT]: { id: ACTION_ID.DELETE_ACCOUNT, label: "Delete Account", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete your account.", requiredPermission: "user.account.delete" },
111
131
  };
112
132
  // Detail page action groups — ordered top-to-bottom in the right-hand panel
@@ -2,6 +2,20 @@ export type SortOption = {
2
2
  readonly value: string;
3
3
  readonly label: string;
4
4
  };
5
+ /** Time + price sorts, the common subset across most listing-type pickers. */
6
+ export declare const BASE_SORT_OPTIONS: readonly [{
7
+ readonly value: string;
8
+ readonly label: "Newest First";
9
+ }, {
10
+ readonly value: string;
11
+ readonly label: "Oldest First";
12
+ }, {
13
+ readonly value: string;
14
+ readonly label: "Price: Low to High";
15
+ }, {
16
+ readonly value: string;
17
+ readonly label: "Price: High to Low";
18
+ }];
5
19
  export declare const STANDARD_SORT_OPTIONS: readonly [{
6
20
  readonly value: string;
7
21
  readonly label: "Newest First";
@@ -108,10 +122,13 @@ export declare const PREORDER_SORT_OPTIONS: readonly [{
108
122
  }, {
109
123
  readonly value: string;
110
124
  readonly label: "Latest Delivery";
111
- }, {
125
+ }, ...({
112
126
  readonly value: string;
113
127
  readonly label: "Newest First";
114
- }, {
128
+ } | {
129
+ readonly value: string;
130
+ readonly label: "Oldest First";
131
+ })[], {
115
132
  readonly value: string;
116
133
  readonly label: "Price: Low to High";
117
134
  }, {
@@ -124,10 +141,13 @@ export declare const PREORDER_SORT_OPTIONS: readonly [{
124
141
  readonly value: string;
125
142
  readonly label: "Lowest Deposit First";
126
143
  }];
127
- export declare const BUNDLE_SORT_OPTIONS: readonly [{
144
+ export declare const BUNDLE_SORT_OPTIONS: readonly [...({
128
145
  readonly value: string;
129
146
  readonly label: "Newest First";
130
- }, {
147
+ } | {
148
+ readonly value: string;
149
+ readonly label: "Oldest First";
150
+ })[], {
131
151
  readonly value: string;
132
152
  readonly label: "Price: Low to High";
133
153
  }, {
@@ -5,13 +5,25 @@ const LABEL_PRICE_LOW = "Price: Low to High";
5
5
  const LABEL_NEWEST = "Newest First";
6
6
  const LABEL_OLDEST = "Oldest First";
7
7
  // ---------------------------------------------------------------------------
8
- // Standard Products
8
+ // W2-6 — Base sort options shared by standard / pre-order / bundle / prize-draw.
9
+ // Auctions get their own base (auctionEndDate primary) because the "Ending Soon"
10
+ // dimension dominates browse intent.
9
11
  // ---------------------------------------------------------------------------
10
- export const STANDARD_SORT_OPTIONS = [
12
+ const BASE_TIME_SORTS = [
11
13
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: LABEL_NEWEST },
12
14
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: LABEL_OLDEST },
15
+ ];
16
+ const BASE_PRICE_SORTS = [
13
17
  { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW },
14
18
  { value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH },
19
+ ];
20
+ /** Time + price sorts, the common subset across most listing-type pickers. */
21
+ export const BASE_SORT_OPTIONS = [...BASE_TIME_SORTS, ...BASE_PRICE_SORTS];
22
+ // ---------------------------------------------------------------------------
23
+ // Standard Products
24
+ // ---------------------------------------------------------------------------
25
+ export const STANDARD_SORT_OPTIONS = [
26
+ ...BASE_SORT_OPTIONS,
15
27
  { value: sortBy(PRODUCT_FIELDS.TITLE, "ASC"), label: "Name: A–Z" },
16
28
  { value: sortBy(PRODUCT_FIELDS.TITLE), label: "Name: Z–A" },
17
29
  { value: sortBy(PRODUCT_FIELDS.FEATURED), label: "Featured First" },
@@ -29,7 +41,7 @@ export const STANDARD_PUBLIC_SORT_OPTIONS = [
29
41
  STANDARD_SORT_OPTIONS[9],
30
42
  ];
31
43
  // ---------------------------------------------------------------------------
32
- // Auctions
44
+ // Auctions — auction-specific base puts auctionEndDate primary
33
45
  // ---------------------------------------------------------------------------
34
46
  export const AUCTION_SORT_OPTIONS = [
35
47
  { value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC"), label: "Ending Soon" },
@@ -56,9 +68,8 @@ export const AUCTION_PUBLIC_SORT_OPTIONS = [
56
68
  export const PREORDER_SORT_OPTIONS = [
57
69
  { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE, "ASC"), label: "Earliest Delivery" },
58
70
  { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE), label: "Latest Delivery" },
59
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: LABEL_NEWEST },
60
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW },
61
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH },
71
+ ...BASE_TIME_SORTS.slice(0, 1), // Newest First
72
+ ...BASE_PRICE_SORTS,
62
73
  { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_CURRENT_COUNT), label: "Fewest Slots Left" },
63
74
  { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DEPOSIT_AMOUNT, "ASC"), label: "Lowest Deposit First" },
64
75
  ];
@@ -66,9 +77,8 @@ export const PREORDER_SORT_OPTIONS = [
66
77
  // Bundles
67
78
  // ---------------------------------------------------------------------------
68
79
  export const BUNDLE_SORT_OPTIONS = [
69
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: LABEL_NEWEST },
70
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: LABEL_PRICE_LOW },
71
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: LABEL_PRICE_HIGH },
80
+ ...BASE_TIME_SORTS.slice(0, 1), // Newest First
81
+ ...BASE_PRICE_SORTS,
72
82
  { value: "-savingsAmount", label: "Most Savings" },
73
83
  { value: "-bundleItemCount", label: "Most Items" },
74
84
  ];
@@ -76,8 +86,7 @@ export const BUNDLE_SORT_OPTIONS = [
76
86
  // Prize Draws
77
87
  // ---------------------------------------------------------------------------
78
88
  export const PRIZE_DRAW_SORT_OPTIONS = [
79
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: LABEL_NEWEST },
80
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: LABEL_OLDEST },
89
+ ...BASE_TIME_SORTS,
81
90
  { value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START, "ASC"), label: "Reveal: Soonest" },
82
91
  { value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START), label: "Reveal: Furthest" },
83
92
  { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Entry: Low to High" },
@@ -256,6 +256,4 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
256
256
  /** Cloud Functions: returns refs for draft products older than `cutoff` (for 30-day prune). */
257
257
  getStaleDraftRefs(cutoff: Date): Promise<DocumentReference[]>;
258
258
  }
259
- export declare class ProductsRepository extends ProductRepository {
260
- }
261
259
  export declare const productRepository: ProductRepository;
@@ -573,6 +573,4 @@ ProductRepository.FILTER_ALIASES = {
573
573
  ].join(",");
574
574
  },
575
575
  };
576
- export class ProductsRepository extends ProductRepository {
577
- }
578
576
  export const productRepository = new ProductRepository();
@@ -132,6 +132,10 @@ export interface ProductDocument {
132
132
  status: ProductStatus;
133
133
  storeId: string;
134
134
  storeName?: string;
135
+ /** W1-34 — denormalized seller rating (0–5) shown on product detail store card. */
136
+ storeRating?: number;
137
+ /** W1-34 — denormalized seller total review count shown alongside the rating. */
138
+ storeReviewCount?: number;
135
139
  featured: boolean;
136
140
  tags: string[];
137
141
  specifications?: ProductSpecification[];
@@ -194,6 +198,19 @@ export interface ProductDocument {
194
198
  groupParentSlug?: string;
195
199
  groupChildSlugs?: string[];
196
200
  groupTitle?: string;
201
+ /**
202
+ * W1-45 — optional icon shown as an overlay badge on every card whose
203
+ * product belongs to this group. Set once on the group parent; cards
204
+ * inherit it via the snapshot pipeline. Either an emoji or a media slug
205
+ * (rendered via `/media/<slug>`).
206
+ */
207
+ groupIcon?: string;
208
+ /**
209
+ * W1-45 — denormalized icon from the sublisting category (`display.icon`).
210
+ * Mirrored onto the product snapshot at create/update time so cards can
211
+ * render it without an extra category lookup per row.
212
+ */
213
+ sublistingIcon?: string;
197
214
  /**
198
215
  * Required since Phase 4 dropped the legacy `isAuction` / `isPreOrder`
199
216
  * booleans. Every product document carries this; queries route through
@@ -89,8 +89,8 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
89
89
  label?: string | undefined;
90
90
  icon?: string | undefined;
91
91
  isActive?: boolean | undefined;
92
- iconColor?: string | undefined;
93
92
  displayOrder?: number | undefined;
93
+ iconColor?: string | undefined;
94
94
  productTypes?: ("auction" | "prize-draw" | "classified" | "digital-code" | "live" | "all" | "product" | "preorder")[] | undefined;
95
95
  }, {
96
96
  category?: "seller" | "custom" | "auction" | "condition" | "shipping" | "preorder" | "platform" | undefined;
@@ -98,8 +98,8 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
98
98
  label?: string | undefined;
99
99
  icon?: string | undefined;
100
100
  isActive?: boolean | undefined;
101
- iconColor?: string | undefined;
102
101
  displayOrder?: number | undefined;
102
+ iconColor?: string | undefined;
103
103
  productTypes?: ("auction" | "prize-draw" | "classified" | "digital-code" | "live" | "all" | "product" | "preorder")[] | undefined;
104
104
  }>;
105
105
  export type ProductFeatureAdminCreatePayload = z.infer<typeof productFeatureAdminCreateSchema>;
@@ -22,7 +22,5 @@ export interface ProductTemplateDocument {
22
22
  createdAt: Date | string;
23
23
  updatedAt: Date | string;
24
24
  }
25
- export interface ProductTemplateCreateInput extends Omit<ProductTemplateDocument, "id" | "createdAt" | "updatedAt"> {
26
- }
27
- export interface ProductTemplateUpdateInput extends Partial<Omit<ProductTemplateDocument, "id" | "storeId" | "createdAt">> {
28
- }
25
+ export type ProductTemplateCreateInput = Omit<ProductTemplateDocument, "id" | "createdAt" | "updatedAt">;
26
+ export type ProductTemplateUpdateInput = Partial<Omit<ProductTemplateDocument, "id" | "storeId" | "createdAt">>;
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ProductRepository, ProductsRepository, productRepository, } from "./repository/products.repository";
7
+ export { ProductRepository, productRepository, } from "./repository/products.repository";
8
8
  export { GET, POST } from "./api/route";
9
9
  export { GET as productItemGET, PATCH as productItemPATCH, DELETE as productItemDELETE, } from "./api/[id]/route";
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ProductRepository, ProductsRepository, productRepository, } from "./repository/products.repository";
7
+ export { ProductRepository, productRepository, } from "./repository/products.repository";
8
8
  export { GET, POST } from "./api/route";
9
9
  export { GET as productItemGET, PATCH as productItemPATCH, DELETE as productItemDELETE, } from "./api/[id]/route";
@@ -65,6 +65,10 @@ export interface ProductItem {
65
65
  groupParentSlug?: string;
66
66
  groupChildSlugs?: string[];
67
67
  groupTitle?: string;
68
+ /** W1-45 — icon shown as a card overlay; emoji or `/media/<slug>`. */
69
+ groupIcon?: string;
70
+ /** W1-45 — denormalized icon from the sublisting category. */
71
+ sublistingIcon?: string;
68
72
  isOnSale?: boolean;
69
73
  isSold?: boolean;
70
74
  inStock?: boolean;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Pencil, Trash2, ToggleLeft, ToggleRight } from "lucide-react";
5
- import { BaseListingCard, Div, Row, Span, Text } from "../../../ui";
5
+ import { BaseListingCard, ConfirmDeleteModal, Div, Row, Span, Text } from "../../../ui";
6
6
  import { useLongPress } from "../../../react/hooks/useLongPress";
7
7
  const TYPE_COLORS = {
8
8
  percentage: { card: "bg-purple-50 border-purple-200 dark:bg-purple-950/30 dark:border-purple-800", code: "bg-purple-100 dark:bg-purple-900/40 text-purple-800 dark:text-purple-200 border-purple-300 dark:border-purple-700" },
@@ -91,6 +91,7 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
91
91
  const [copied, setCopied] = useState(false);
92
92
  const [claiming, setClaiming] = useState(false);
93
93
  const [busy, setBusy] = useState(null);
94
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
94
95
  const longPress = useLongPress(() => onSelect?.(n.id, !isSelected));
95
96
  const expiry = formatDateSafe(n.expiresAt);
96
97
  const discountLabel = formatDiscount(n, labels);
@@ -144,11 +145,10 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
144
145
  setBusy(null);
145
146
  }
146
147
  };
147
- const handleDelete = async () => {
148
+ const handleDeleteConfirmed = async () => {
148
149
  if (!onDelete)
149
150
  return;
150
- if (typeof window !== "undefined" && !window.confirm(labels.deleteConfirm))
151
- return;
151
+ setDeleteConfirmOpen(false);
152
152
  setBusy("delete");
153
153
  try {
154
154
  await onDelete(n.id);
@@ -161,5 +161,5 @@ export function CouponCard({ coupon, labels: labelsProp, onCopy, className = "",
161
161
  ? "bg-success-surface text-success"
162
162
  : "bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300"}`, children: n.isActive ? labels.active : labels.inactive }), n.scope && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-zinc-200/70 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-zinc-700 dark:bg-zinc-700/60 dark:text-zinc-200", children: n.scope }))] })), _jsxs(Div, { className: "mb-3", children: [_jsx(Text, { className: "text-2xl font-extrabold tracking-tight leading-none", children: discountLabel }), n.name && (_jsx(Text, { className: "text-sm mt-0.5 font-medium opacity-80", children: n.name }))] }), n.description && (_jsx(Text, { className: "text-xs opacity-60 mb-3", children: n.description })), _jsxs("div", { className: `flex items-center gap-2 rounded-lg border border-dashed px-3 py-2 ${colors.code}`, children: [_jsx("span", { className: "flex-1 font-mono text-sm font-bold tracking-widest uppercase select-all", children: n.code || "—" }), n.code && (_jsx("button", { type: "button", onClick: handleCopy, className: "shrink-0 rounded-md px-2.5 py-1 text-xs font-semibold transition-all hover:opacity-80 active:scale-95 bg-white/60 dark:bg-black/20 border border-current/20", "aria-label": "Copy coupon code", children: copied ? labels.copied : labels.copy }))] }), showClaim && n.code && n.isActive && (_jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", onClick: handleClaim, disabled: claiming, className: "w-full rounded-md px-3 py-1.5 text-xs font-semibold transition-all active:scale-95 bg-[var(--appkit-color-primary-700)] text-[var(--appkit-color-text-on-primary,#fff)] hover:bg-[var(--appkit-color-primary-800)] disabled:opacity-50", "aria-label": `Claim coupon ${n.code} and apply at checkout`, children: claiming ? labels.claiming : labels.claim }) })), _jsxs(Row, { wrap: true, gap: "sm", className: "text-xs opacity-60 mt-2", children: [n.minOrderAmount ? (_jsxs(Span, { children: [labels.minOrder, ": \u20B9", (n.minOrderAmount / 100).toFixed(0)] })) : null, expiry && (_jsxs(Span, { children: [labels.expires, ": ", expiry] })), n.usageLimit ? (_jsxs(Span, { children: [n.usageCount, "/", n.usageLimit, " ", labels.used] })) : n.usageCount > 0 ? (_jsxs(Span, { children: [n.usageCount, " ", labels.used] })) : null] }), hasAdminActions && (_jsxs(Row, { gap: "xs", justify: "end", className: "mt-auto pt-3", children: [onEdit && (_jsx("button", { type: "button", onClick: () => onEdit(n.id), title: labels.edit, "aria-label": labels.edit, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 hover:text-zinc-800 dark:hover:text-zinc-200 transition-colors", children: _jsx(Pencil, { className: "h-4 w-4" }) })), onToggleActive && (_jsx("button", { type: "button", onClick: handleToggle, disabled: busy === "toggle", title: n.isActive ? labels.deactivate : labels.activate, "aria-label": n.isActive ? labels.deactivate : labels.activate, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-white/60 dark:hover:bg-black/30 transition-colors disabled:opacity-50", children: n.isActive
163
163
  ? _jsx(ToggleRight, { className: "h-4 w-4 text-emerald-600 dark:text-emerald-400" })
164
- : _jsx(ToggleLeft, { className: "h-4 w-4" }) })), onDelete && (_jsx("button", { type: "button", onClick: handleDelete, disabled: busy === "delete", title: labels.delete, "aria-label": labels.delete, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-red-50 dark:hover:bg-red-950/40 hover:text-red-600 dark:hover:text-red-400 transition-colors disabled:opacity-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }))] }));
164
+ : _jsx(ToggleLeft, { className: "h-4 w-4" }) })), onDelete && (_jsx("button", { type: "button", onClick: () => setDeleteConfirmOpen(true), disabled: busy === "delete", title: labels.delete, "aria-label": labels.delete, className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-red-50 dark:hover:bg-red-950/40 hover:text-red-600 dark:hover:text-red-400 transition-colors disabled:opacity-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Coupon", message: labels.deleteConfirm, onConfirm: handleDeleteConfirmed, onClose: () => setDeleteConfirmOpen(false), isDeleting: busy === "delete" }))] }));
165
165
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback, useMemo } from "react";
4
4
  import { Search, SlidersHorizontal, X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { Pagination, SortDropdown, Div, Text, Heading } from "../../../ui";
6
+ import { ListingFilterDrawer, Pagination, SortDropdown, Div, Text, Heading } from "../../../ui";
7
7
  import { usePromotions } from "../hooks/usePromotions";
8
8
  import { CouponCard } from "./CouponCard";
9
9
  import { TABLE_KEYS } from "../../../constants/table-keys";
@@ -34,6 +34,7 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
34
34
  get: (key) => pendingFilters[key] ?? "",
35
35
  set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
36
36
  }), [pendingFilters]);
37
+ const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
37
38
  const openFilters = useCallback(() => {
38
39
  setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
39
40
  setFilterOpen(true);
@@ -97,5 +98,5 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
97
98
  minOrder: "Min. order",
98
99
  off: "OFF",
99
100
  freeShipping: "Free Shipping",
100
- } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-6", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: pendingTable.get(TABLE_KEYS.TYPE) === t.value, onChange: () => { pendingTable.set(TABLE_KEYS.TYPE, t.value); }, className: "accent-primary" }), t.label] }, t.value))), pendingTable.get(TABLE_KEYS.TYPE) && (_jsx("button", { type: "button", onClick: () => { pendingTable.set(TABLE_KEYS.TYPE, ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] }), _jsxs("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5 flex gap-2", children: [_jsx("button", { type: "button", onClick: clearPending, className: "flex-1 rounded-lg border border-zinc-300 dark:border-slate-600 py-2.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: applyFilters, className: "flex-1 rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply Filters" })] })] })] }))] }));
101
+ } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearPending, activeCount: activeFilterCount, children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: pendingTable.get(TABLE_KEYS.TYPE) === t.value, onChange: () => { pendingTable.set(TABLE_KEYS.TYPE, t.value); }, className: "accent-primary" }), t.label] }, t.value))), pendingTable.get(TABLE_KEYS.TYPE) && (_jsx("button", { type: "button", onClick: () => { pendingTable.set(TABLE_KEYS.TYPE, ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs(_Fragment, { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] })] }));
101
102
  }
@@ -4,6 +4,5 @@
4
4
  * Server-only entry point for promotions repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { PromotionsRepository } from "./repository/promotions.repository";
8
7
  export { couponsRepository } from "./repository/coupons.repository";
9
8
  export { GET as promotionsGET, GET } from "./api/route";
@@ -4,6 +4,5 @@
4
4
  * Server-only entry point for promotions repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { PromotionsRepository } from "./repository/promotions.repository";
8
7
  export { couponsRepository } from "./repository/coupons.repository";
9
8
  export { GET as promotionsGET, GET } from "./api/route";
@@ -1,9 +1,8 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback, useMemo } from "react";
4
- import { X } from "lucide-react";
5
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { ListingToolbar, Pagination, Text } from "../../../ui";
5
+ import { ListingFilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
7
6
  import { ReviewCard } from "./ReviewsList";
8
7
  import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
9
8
  import { useReviews } from "../hooks/useReviews";
@@ -103,5 +102,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
103
102
  value: opt.value,
104
103
  label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
105
104
  }));
106
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
105
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) })] }));
107
106
  }
@@ -106,4 +106,3 @@ declare class ReviewRepository extends BaseRepository<ReviewDocument> {
106
106
  }
107
107
  declare const reviewRepository: ReviewRepository;
108
108
  export { ReviewRepository, reviewRepository };
109
- export { ReviewRepository as ReviewsRepository };
@@ -233,4 +233,3 @@ ReviewRepository.SIEVE_FIELDS = {
233
233
  };
234
234
  const reviewRepository = new ReviewRepository();
235
235
  export { ReviewRepository, reviewRepository };
236
- export { ReviewRepository as ReviewsRepository };
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ReviewRepository, ReviewsRepository, reviewRepository, } from "./repository/reviews.repository";
7
+ export { ReviewRepository, reviewRepository, } from "./repository/reviews.repository";
8
8
  export { GET as reviewsGET, GET, POST } from "./api/route";
9
9
  export { reviewItemGET, reviewItemPATCH, reviewItemDELETE, } from "./api/[id]/route";
@@ -4,6 +4,6 @@
4
4
  * Server-only entry point — repositories and API route handlers.
5
5
  */
6
6
  export * from "./actions";
7
- export { ReviewRepository, ReviewsRepository, reviewRepository, } from "./repository/reviews.repository";
7
+ export { ReviewRepository, reviewRepository, } from "./repository/reviews.repository";
8
8
  export { GET as reviewsGET, GET, POST } from "./api/route";
9
9
  export { reviewItemGET, reviewItemPATCH, reviewItemDELETE, } from "./api/[id]/route";