@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
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { useMutation, useQuery } from "@tanstack/react-query";
5
- import { Button, Form, Input, Select, StackedViewShell, Toggle, useToast, } from "../../../ui";
5
+ import { Button, ConfirmDeleteModal, Form, Input, Select, StackedViewShell, Toggle, useToast, } from "../../../ui";
6
6
  import { FieldInput, FormShellContext, useFormShellState } from "../../../ui/forms";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
@@ -60,6 +60,7 @@ export function AdminCouponEditorView({ couponId, onSaved, onDeleted, embedded,
60
60
  const [firstTimeOnly, setFirstTimeOnly] = React.useState(false);
61
61
  const [combinable, setCombinable] = React.useState(false);
62
62
  const [appliesToAuctions, setAppliesToAuctions] = React.useState(false);
63
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
63
64
  const { showToast } = useToast();
64
65
  const { shellCtx, setFieldError, clearErrors } = useFormShellState();
65
66
  // --- load existing data (edit mode) ---
@@ -171,22 +172,18 @@ export function AdminCouponEditorView({ couponId, onSaved, onDeleted, embedded,
171
172
  : type === "fixed"
172
173
  ? "Discount amount (paise)"
173
174
  : "Discount value";
174
- const formSection = (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Form, { onSubmit: (e) => {
175
- e.preventDefault();
176
- clearErrors();
177
- if (!name.trim()) {
178
- setFieldError("name", "Campaign name is required");
179
- return;
180
- }
181
- saveMutation.mutate();
182
- }, className: "space-y-5", children: [_jsx(Select, { label: "Coupon type", options: TYPE_OPTIONS, value: type, onValueChange: (v) => setType(v), required: true }), _jsx(FieldInput, { name: "name", label: "Campaign name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Summer Sale 20%" }), !isEdit && (_jsx(Input, { label: "Coupon code", value: code, onChange: (e) => {
183
- setCode(toCouponCode(e.target.value));
184
- setCodeManual(true);
185
- }, required: true, placeholder: "e.g. SUMMER20", helperText: "Auto-generated from name. Uppercase alphanumeric + hyphens only." })), isEdit && (_jsx(Input, { label: "Coupon code", value: code, disabled: true, helperText: "Code cannot be changed after creation." })), _jsx(Input, { label: "Description (optional)", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Internal notes about this coupon" }), _jsx(CouponDiscountFields, { type: type, discountValue: discountValue, setDiscountValue: setDiscountValue, maxDiscount: maxDiscount, setMaxDiscount: setMaxDiscount, minPurchase: minPurchase, setMinPurchase: setMinPurchase, buyQty: buyQty, setBuyQty: setBuyQty, getQty: getQty, setGetQty: setGetQty, discountLabel: discountLabel }), _jsx(CouponValidityFields, { startDate: startDate, setStartDate: setStartDate, endDate: endDate, setEndDate: setEndDate, isActive: isActive, setIsActive: setIsActive, totalLimit: totalLimit, setTotalLimit: setTotalLimit, perUserLimit: perUserLimit, setPerUserLimit: setPerUserLimit, isEdit: isEdit, currentUsage: currentUsage }), _jsx(Toggle, { label: "First-time users only", checked: firstTimeOnly, onChange: setFirstTimeOnly }), _jsx(Toggle, { label: "Allow stacking with seller coupons", checked: combinable, onChange: setCombinable }), _jsx(Toggle, { label: "Applies to auctions", checked: appliesToAuctions, onChange: setAppliesToAuctions }), _jsxs("div", { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? "Save changes" : "Create coupon" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => {
186
- if (confirm("Delete this coupon? This cannot be undone.")) {
187
- deleteMutation.mutate();
188
- }
189
- }, children: "Delete coupon" }))] })] }, "coupon-form") }));
175
+ const formSection = (_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsxs(Form, { onSubmit: (e) => {
176
+ e.preventDefault();
177
+ clearErrors();
178
+ if (!name.trim()) {
179
+ setFieldError("name", "Campaign name is required");
180
+ return;
181
+ }
182
+ saveMutation.mutate();
183
+ }, className: "space-y-5", children: [_jsx(Select, { label: "Coupon type", options: TYPE_OPTIONS, value: type, onValueChange: (v) => setType(v), required: true }), _jsx(FieldInput, { name: "name", label: "Campaign name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Summer Sale 20%" }), !isEdit && (_jsx(Input, { label: "Coupon code", value: code, onChange: (e) => {
184
+ setCode(toCouponCode(e.target.value));
185
+ setCodeManual(true);
186
+ }, required: true, placeholder: "e.g. SUMMER20", helperText: "Auto-generated from name. Uppercase alphanumeric + hyphens only." })), isEdit && (_jsx(Input, { label: "Coupon code", value: code, disabled: true, helperText: "Code cannot be changed after creation." })), _jsx(Input, { label: "Description (optional)", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Internal notes about this coupon" }), _jsx(CouponDiscountFields, { type: type, discountValue: discountValue, setDiscountValue: setDiscountValue, maxDiscount: maxDiscount, setMaxDiscount: setMaxDiscount, minPurchase: minPurchase, setMinPurchase: setMinPurchase, buyQty: buyQty, setBuyQty: setBuyQty, getQty: getQty, setGetQty: setGetQty, discountLabel: discountLabel }), _jsx(CouponValidityFields, { startDate: startDate, setStartDate: setStartDate, endDate: endDate, setEndDate: setEndDate, isActive: isActive, setIsActive: setIsActive, totalLimit: totalLimit, setTotalLimit: setTotalLimit, perUserLimit: perUserLimit, setPerUserLimit: setPerUserLimit, isEdit: isEdit, currentUsage: currentUsage }), _jsx(Toggle, { label: "First-time users only", checked: firstTimeOnly, onChange: setFirstTimeOnly }), _jsx(Toggle, { label: "Allow stacking with seller coupons", checked: combinable, onChange: setCombinable }), _jsx(Toggle, { label: "Applies to auctions", checked: appliesToAuctions, onChange: setAppliesToAuctions }), _jsxs("div", { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? "Save changes" : "Create coupon" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteConfirmOpen(true), children: "Delete coupon" }))] })] }, "coupon-form"), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Coupon", message: "Delete this coupon? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(); setDeleteConfirmOpen(false); }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
190
187
  if (embedded) {
191
188
  return _jsx("div", { className: "overflow-y-auto p-4", children: formSection });
192
189
  }
@@ -1,9 +1,6 @@
1
- import type { ListingViewShellProps } from "../../../ui";
2
- import type { AdminListingScaffoldRow } from "./AdminListingScaffold";
3
- export interface AdminCouponsViewProps extends ListingViewShellProps {
4
- actionHref?: string;
5
- getRowHref?: (row: AdminListingScaffoldRow) => string;
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export interface AdminCouponsViewProps extends ListingLayoutProps {
6
3
  onBulkArchive?: (ids: string[]) => Promise<void>;
7
4
  onBulkDelete?: (ids: string[]) => Promise<void>;
8
5
  }
9
- export declare function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }: AdminCouponsViewProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...props }: AdminCouponsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -1,125 +1,97 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import React, { useState, useCallback } from "react";
4
- import { Plus, X } from "lucide-react";
5
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
8
- import { BulkActionBar, Button, Div, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer, Text, useToast } from "../../../ui";
9
- import { useBottomActions } from "../../layout";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React, { useCallback } from "react";
4
+ import { useQueryClient } from "@tanstack/react-query";
5
+ import { Div, FilterChipGroup, ListingLayout, Text, useToast } from "../../../ui";
10
6
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
7
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
8
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
9
+ import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
12
10
  import { ADMIN_COUPON_TYPE_TABS } from "../constants/filter-tabs";
13
11
  import { apiClient } from "../../../http";
14
- import { toRecordArray, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
12
+ import { toRecordArray, toStringValue, } from "../hooks/useAdminListingData";
13
+ import { DataListingView } from "./DataListingView";
15
14
  import { AdminCouponEditorView } from "./AdminCouponEditorView";
16
15
  import { CouponCard } from "../../promotions/components/CouponCard";
17
- const PAGE_SIZE = 25;
18
- const FILTER_KEYS = ["type"];
19
- const DEFAULT_SORT = "-createdAt";
20
- const SORT_OPTIONS = [
21
- { value: "-createdAt", label: "Newest" },
22
- { value: "createdAt", label: "Oldest" },
23
- { value: "code", label: "Code A–Z" },
24
- ];
25
- const TYPE_OPTIONS = ADMIN_COUPON_TYPE_TABS;
26
- function CouponsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
27
- if (!filterOpen)
28
- return null;
29
- return (_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: "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", 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 space-y-5", children: _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }) }), _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})` : ""] }) })] })] }));
30
- }
31
- export function AdminCouponsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
32
- const hasChildren = React.Children.count(children) > 0;
33
- const [view, setView] = useState("table");
34
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
35
- const { openCreatePanel, openEditPanel, closePanel, isCreateOpen, isEditOpen, editId } = usePanelUrlSync();
36
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
37
- const [filterOpen, setFilterOpen] = useState(false);
38
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
39
- const openFilters = useCallback(() => {
40
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
41
- setFilterOpen(true);
42
- }, [table]);
43
- const applyFilters = useCallback(() => {
44
- const updates = { page: "1" };
45
- for (const k of FILTER_KEYS)
46
- updates[k] = pendingFilters[k] ?? "";
47
- table.setMany(updates);
48
- setFilterOpen(false);
49
- }, [pendingFilters, table]);
50
- const clearFilters = useCallback(() => {
51
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
52
- }, []);
53
- const resetAll = useCallback(() => {
54
- const updates = { q: "", sort: "" };
55
- for (const k of FILTER_KEYS)
56
- updates[k] = "";
57
- table.setMany(updates);
58
- setSearchInput("");
59
- }, [table]);
60
- const commitSearch = useCallback(() => {
61
- table.set("q", searchInput.trim());
62
- }, [searchInput, table]);
63
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
64
- const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
65
- const typeRaw = table.get("type");
66
- const filters = typeRaw && typeRaw !== "All" ? `type==${typeRaw}` : undefined;
67
- const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
68
- queryKey: ["admin", "coupons", "listing"],
69
- endpoint: ADMIN_ENDPOINTS.COUPONS,
70
- page: table.getNumber("page", 1),
71
- pageSize: PAGE_SIZE,
72
- sorts: table.get("sort") || DEFAULT_SORT,
73
- filters,
74
- q: table.get("q") || undefined,
75
- mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
76
- id: toStringValue(item.id, `coupon-${index}`),
77
- raw: item,
78
- })),
79
- getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
80
- });
16
+ export function AdminCouponsView({ children, onBulkArchive, onBulkDelete, ...props }) {
81
17
  const { showToast } = useToast();
18
+ const queryClient = useQueryClient();
82
19
  const handleToggle = useCallback(async (id, currentlyActive) => {
83
20
  try {
84
21
  await apiClient.patch(ADMIN_ENDPOINTS.COUPON_BY_ID(id), {
85
22
  validity: { isActive: !currentlyActive },
86
23
  });
87
24
  showToast(currentlyActive ? "Coupon deactivated." : "Coupon activated.", "success");
88
- refetch();
25
+ queryClient.invalidateQueries({ queryKey: ["admin", "coupons"] });
89
26
  }
90
27
  catch {
91
28
  showToast("Could not update coupon status.", "error");
92
29
  }
93
- }, [showToast, refetch]);
30
+ }, [showToast, queryClient]);
94
31
  const handleDelete = useCallback(async (id) => {
95
32
  try {
96
33
  await apiClient.delete(ADMIN_ENDPOINTS.COUPON_BY_ID(id));
97
34
  showToast("Coupon deleted.", "success");
98
- refetch();
35
+ queryClient.invalidateQueries({ queryKey: ["admin", "coupons"] });
99
36
  }
100
37
  catch {
101
38
  showToast("Could not delete coupon.", "error");
102
39
  }
103
- }, [showToast, refetch]);
104
- const currentPage = table.getNumber("page", 1);
105
- const totalPages = Math.ceil(total / PAGE_SIZE);
106
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
107
- const bulkActions = [
108
- ...(onBulkArchive ? [{
109
- id: "bulk-archive",
110
- label: "Archive",
111
- onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); },
112
- }] : []),
113
- ...(onBulkDelete ? [{
114
- id: "bulk-delete",
115
- label: ACTIONS.ADMIN["delete-coupon"].label,
116
- variant: "danger",
117
- onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
118
- }] : []),
119
- ];
120
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
121
- if (hasChildren) {
122
- return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
40
+ }, [showToast, queryClient]);
41
+ if (React.Children.count(children) > 0) {
42
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
123
43
  }
124
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search codes, campaigns, or seller scopes", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Coupon"] }) }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-400", children: "No coupons found" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) }))] }), _jsx(CouponsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Coupon" : "Edit Coupon", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCouponEditorView, { couponId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
44
+ const config = {
45
+ portal: "admin",
46
+ title: "Coupons",
47
+ searchPlaceholder: "Search codes, campaigns, or seller scopes",
48
+ emptyLabel: "No coupons found",
49
+ filterKeys: ["type"],
50
+ defaultSort: "-createdAt",
51
+ queryKey: ["admin", "coupons", "listing"],
52
+ endpoint: ADMIN_ENDPOINTS.COUPONS,
53
+ sortOptions: [
54
+ { value: "-createdAt", label: "Newest" },
55
+ { value: "createdAt", label: "Oldest" },
56
+ { value: "code", label: "Code A–Z" },
57
+ ],
58
+ hideTableView: true,
59
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
60
+ id: toStringValue(item.id, `coupon-${index}`),
61
+ raw: item,
62
+ })),
63
+ getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
64
+ buildFilters: (state) => state.type && state.type !== "All" ? `type==${state.type}` : undefined,
65
+ primaryAction: {
66
+ label: "Add Coupon",
67
+ onClick: ({ openCreatePanel }) => openCreatePanel(),
68
+ },
69
+ buildBulkActions: (selection) => [
70
+ ...(onBulkArchive
71
+ ? [
72
+ {
73
+ id: "bulk-archive",
74
+ label: ROW_ACTION_META[ROW_ACTION_ID.ARCHIVE].label,
75
+ onClick: async () => {
76
+ await onBulkArchive(selection.selectedIds);
77
+ selection.clearSelection();
78
+ },
79
+ },
80
+ ]
81
+ : []),
82
+ ...(onBulkDelete
83
+ ? [
84
+ buildBulkAction(ACTIONS.ADMIN["delete-coupon"], async () => {
85
+ await onBulkDelete(selection.selectedIds);
86
+ selection.clearSelection();
87
+ }),
88
+ ]
89
+ : []),
90
+ ],
91
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_COUPON_TYPE_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })),
92
+ renderCards: (rows, _view, selectionCtx, isLoading) => isLoading ? (_jsx(Div, { className: "fluid-grid-card gap-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Div, { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx(Div, { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx(Div, { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx(Div, { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-400", children: "No coupons found" }) })) : (_jsx(Div, { className: "fluid-grid-card gap-3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: selectionCtx.openEditPanel, onToggleActive: handleToggle, onDelete: handleDelete }, row.id))) })),
93
+ renderEditor: ({ editId, closePanel }) => (_jsx(AdminCouponEditorView, { couponId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
94
+ resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Coupon" : "Edit Coupon"),
95
+ };
96
+ return _jsx(DataListingView, { config: config });
125
97
  }
@@ -0,0 +1,3 @@
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export type AdminDigitalCodesViewProps = ListingLayoutProps;
3
+ export declare function AdminDigitalCodesView({ children, ...props }: AdminDigitalCodesViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * AdminDigitalCodesView — admin browse of digital-code listings (W1-29).
5
+ */
6
+ import React from "react";
7
+ import { ListingLayout } from "../../../ui";
8
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { toRecordArray, toRelativeDate, toRupees, toStringValue, } from "../hooks/useAdminListingData";
10
+ import { DataListingView } from "./DataListingView";
11
+ const ADMIN_DIGITAL_CODES_CONFIG = {
12
+ portal: "admin",
13
+ title: "Digital Codes",
14
+ searchPlaceholder: "Search digital code listings",
15
+ emptyLabel: "No digital code listings",
16
+ filterKeys: [],
17
+ defaultSort: "-createdAt",
18
+ queryKey: ["admin", "digital-codes", "listing"],
19
+ endpoint: ADMIN_ENDPOINTS.PRODUCTS,
20
+ sortOptions: [
21
+ { value: "-createdAt", label: "Newest" },
22
+ { value: "createdAt", label: "Oldest" },
23
+ { value: "title", label: "Title A–Z" },
24
+ ],
25
+ mapRows: (response) => toRecordArray(response.items).map((item, index) => {
26
+ const dc = (item.digitalCode ?? {});
27
+ return {
28
+ id: toStringValue(item.id, `dc-${index}`),
29
+ primary: toStringValue(item.title ?? item.productTitle, "Untitled digital code"),
30
+ secondary: [
31
+ toStringValue(item.sellerName, "Unknown seller"),
32
+ toRupees(item.price),
33
+ typeof dc.codesAvailable === "number"
34
+ ? `${dc.codesAvailable} avail`
35
+ : null,
36
+ ]
37
+ .filter(Boolean)
38
+ .join(" · "),
39
+ status: toStringValue(item.status, "draft"),
40
+ updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
41
+ };
42
+ }),
43
+ getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
44
+ buildFilters: () => "listingType==digital-code",
45
+ };
46
+ export function AdminDigitalCodesView({ children, ...props }) {
47
+ if (React.Children.count(children) > 0) {
48
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
49
+ }
50
+ return _jsx(DataListingView, { config: ADMIN_DIGITAL_CODES_CONFIG });
51
+ }
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { useMutation, useQuery } from "@tanstack/react-query";
5
- import { Button, Form, Input, RichTextEditor, Select, StackedViewShell, TagInput, Text, Toggle, useToast } from "../../../ui";
5
+ import { Button, ConfirmDeleteModal, Form, Input, RichTextEditor, Select, StackedViewShell, TagInput, Text, Toggle, useToast } from "../../../ui";
6
6
  import { FieldInput, FormShellContext, useFormShellState } from "../../../ui/forms";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
@@ -38,6 +38,7 @@ export function AdminFaqEditorView({ faqId, onSaved, onDeleted, embedded, ...res
38
38
  const [isPinned, setIsPinned] = React.useState(false);
39
39
  const [showOnHomepage, setShowOnHomepage] = React.useState(false);
40
40
  const [showInFooter, setShowInFooter] = React.useState(false);
41
+ const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
41
42
  const { showToast } = useToast();
42
43
  const { shellCtx, setFieldError, clearErrors } = useFormShellState();
43
44
  // --- load existing FAQ (edit mode) ---
@@ -114,22 +115,18 @@ export function AdminFaqEditorView({ faqId, onSaved, onDeleted, embedded, ...res
114
115
  });
115
116
  const isSubmitting = saveMutation.isPending || faqQuery.isLoading;
116
117
  const canSave = Boolean(question.trim()) && Boolean(answer.trim());
117
- const formSection = (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Form, { onSubmit: (e) => {
118
- e.preventDefault();
119
- clearErrors();
120
- if (!question.trim()) {
121
- setFieldError("question", "Question is required");
122
- return;
123
- }
124
- saveMutation.mutate();
125
- }, className: "space-y-5", children: [_jsx(FieldInput, { name: "question", label: "Question", value: question, onChange: (v) => handleQuestionChange(v), required: true, placeholder: "e.g. How does bidding work on LetItRip?" }), _jsx(Input, { label: "Slug", value: slug, onChange: (e) => {
126
- setSlug(e.target.value);
127
- setSlugManual(true);
128
- }, placeholder: "faq-how-does-bidding-work", helperText: "Auto-generated from question. Must start with 'faq-'." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Answer" }), _jsx(RichTextEditor, { value: answer, onChange: setAnswer, placeholder: "Write a clear, helpful answer...", minHeightClassName: "min-h-[200px]" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: category, onValueChange: setCategory }), _jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within category." })] }), _jsx(Input, { label: "Priority", value: String(priority), onChange: (e) => setPriority(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Higher priority FAQs appear first in search results." }), _jsx(TagInput, { label: "Tags", value: tags, onChange: setTags, placeholder: "e.g. shipping, pokemon, returns" }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to users)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Pinned (always shown at top)", checked: isPinned, onChange: setIsPinned }), _jsx(Toggle, { label: "Show on homepage FAQ section", checked: showOnHomepage, onChange: setShowOnHomepage }), _jsx(Toggle, { label: "Show in footer FAQ links", checked: showInFooter, onChange: setShowInFooter })] }), _jsxs("div", { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? "Save changes" : "Create FAQ" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => {
129
- if (confirm("Delete this FAQ? This cannot be undone.")) {
130
- deleteMutation.mutate();
131
- }
132
- }, children: "Delete FAQ" }))] })] }, "faq-form") }));
118
+ const formSection = (_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsxs(Form, { onSubmit: (e) => {
119
+ e.preventDefault();
120
+ clearErrors();
121
+ if (!question.trim()) {
122
+ setFieldError("question", "Question is required");
123
+ return;
124
+ }
125
+ saveMutation.mutate();
126
+ }, className: "space-y-5", children: [_jsx(FieldInput, { name: "question", label: "Question", value: question, onChange: (v) => handleQuestionChange(v), required: true, placeholder: "e.g. How does bidding work on LetItRip?" }), _jsx(Input, { label: "Slug", value: slug, onChange: (e) => {
127
+ setSlug(e.target.value);
128
+ setSlugManual(true);
129
+ }, placeholder: "faq-how-does-bidding-work", helperText: "Auto-generated from question. Must start with 'faq-'." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Answer" }), _jsx(RichTextEditor, { value: answer, onChange: setAnswer, placeholder: "Write a clear, helpful answer...", minHeightClassName: "min-h-[200px]" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: category, onValueChange: setCategory }), _jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within category." })] }), _jsx(Input, { label: "Priority", value: String(priority), onChange: (e) => setPriority(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Higher priority FAQs appear first in search results." }), _jsx(TagInput, { label: "Tags", value: tags, onChange: setTags, placeholder: "e.g. shipping, pokemon, returns" }), _jsxs("div", { className: "space-y-3 rounded-lg border border-zinc-200 dark:border-zinc-700 p-4", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to users)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Pinned (always shown at top)", checked: isPinned, onChange: setIsPinned }), _jsx(Toggle, { label: "Show on homepage FAQ section", checked: showOnHomepage, onChange: setShowOnHomepage }), _jsx(Toggle, { label: "Show in footer FAQ links", checked: showInFooter, onChange: setShowInFooter })] }), _jsxs("div", { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? "Save changes" : "Create FAQ" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteConfirmOpen(true), children: "Delete FAQ" }))] })] }, "faq-form"), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete FAQ", message: "Delete this FAQ? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(); setDeleteConfirmOpen(false); }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
133
130
  if (embedded) {
134
131
  return _jsx("div", { className: "overflow-y-auto p-4", children: formSection });
135
132
  }
@@ -1,9 +1,9 @@
1
- import type { ListingViewShellProps } from "../../../ui";
2
- import type { AdminListingScaffoldRow } from "./AdminListingScaffold";
3
- export interface AdminFaqsViewProps extends ListingViewShellProps {
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ import type { AdminListingScaffoldRow } from "./DataListingView";
3
+ export interface AdminFaqsViewProps extends ListingLayoutProps {
4
4
  actionHref?: string;
5
5
  getRowHref?: (row: AdminListingScaffoldRow) => string;
6
6
  onBulkArchive?: (ids: string[]) => Promise<void>;
7
7
  onBulkDelete?: (ids: string[]) => Promise<void>;
8
8
  }
9
- export declare function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }: AdminFaqsViewProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function AdminFaqsView({ children, onBulkArchive, onBulkDelete, ...props }: AdminFaqsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -1,103 +1,96 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import React, { useState, useCallback } from "react";
4
- import { Plus, X } from "lucide-react";
5
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
8
- import { BulkActionBar, Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
9
- import { useBottomActions } from "../../layout";
3
+ import React, { useMemo } from "react";
4
+ import { Heading, ListingLayout, Span, Text } from "../../../ui";
10
5
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
6
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
12
- import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
13
- import { DataTable } from "./DataTable";
14
- import { AdminViewCards } from "./AdminViewCards";
7
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
8
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
9
+ import { DataListingView } from "./DataListingView";
15
10
  import { AdminFaqEditorView } from "./AdminFaqEditorView";
16
- const PAGE_SIZE = 25;
17
- const FILTER_KEYS = ["isActive"];
18
- const DEFAULT_SORT = "priority";
19
- const SORT_OPTIONS = [
20
- { value: "priority", label: "Priority" },
21
- { value: "-createdAt", label: "Newest" },
22
- { value: "question", label: "Question A–Z" },
11
+ const COLUMNS = [
12
+ {
13
+ key: "primary",
14
+ header: "Question",
15
+ sortable: true,
16
+ render: (row) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "font-semibold text-zinc-900 dark:text-zinc-100", children: row.primary }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.secondary })] })),
17
+ },
18
+ {
19
+ key: "status",
20
+ header: "Status",
21
+ className: "w-32",
22
+ render: (row) => (_jsx(Span, { className: "inline-flex rounded-full bg-primary-50 px-2.5 py-1 text-xs font-medium text-primary-800 dark:bg-secondary-900/30 dark:text-secondary-300", children: row.status })),
23
+ },
24
+ {
25
+ key: "updatedAt",
26
+ header: "Updated",
27
+ className: "w-32",
28
+ render: (row) => (_jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
29
+ },
23
30
  ];
24
- function FaqsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
25
- if (!filterOpen)
26
- return null;
27
- return (_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: "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", 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 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Published", value: "true" }, { label: "Draft", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _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})` : ""] }) })] })] }));
28
- }
29
- export function AdminFaqsView({ children, getRowHref, onBulkArchive, onBulkDelete, ...props }) {
30
- const hasChildren = React.Children.count(children) > 0;
31
- const [view, setView] = useState("table");
32
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
33
- const { openCreatePanel, openEditPanel, closePanel, isCreateOpen, isEditOpen, editId } = usePanelUrlSync();
34
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
35
- const [filterOpen, setFilterOpen] = useState(false);
36
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
37
- const openFilters = useCallback(() => {
38
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
39
- setFilterOpen(true);
40
- }, [table]);
41
- const applyFilters = useCallback(() => {
42
- const updates = { page: "1" };
43
- for (const k of FILTER_KEYS)
44
- updates[k] = pendingFilters[k] ?? "";
45
- table.setMany(updates);
46
- setFilterOpen(false);
47
- }, [pendingFilters, table]);
48
- const clearFilters = useCallback(() => {
49
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
50
- }, []);
51
- const resetAll = useCallback(() => {
52
- const updates = { q: "", sort: "" };
53
- for (const k of FILTER_KEYS)
54
- updates[k] = "";
55
- table.setMany(updates);
56
- setSearchInput("");
57
- }, [table]);
58
- const commitSearch = useCallback(() => {
59
- table.set("q", searchInput.trim());
60
- }, [searchInput, table]);
61
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
62
- const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
63
- const isActiveRaw = table.get("isActive");
64
- const filters = isActiveRaw ? `isActive==${isActiveRaw}` : undefined;
65
- const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
31
+ export function AdminFaqsView({ children, onBulkArchive, onBulkDelete, ...props }) {
32
+ const config = useMemo(() => ({
33
+ portal: "admin",
34
+ title: "FAQs",
35
+ searchPlaceholder: "Search questions, categories, or tokens",
36
+ emptyLabel: "No FAQs found",
37
+ filterKeys: ["isActive"],
38
+ defaultSort: "priority",
66
39
  queryKey: ["admin", "faqs", "listing"],
67
40
  endpoint: ADMIN_ENDPOINTS.FAQS,
68
- page: table.getNumber("page", 1),
69
- pageSize: PAGE_SIZE,
70
- sorts: table.get("sort") || DEFAULT_SORT,
71
- filters,
72
- q: table.get("q") || undefined,
41
+ sortOptions: [
42
+ { value: "priority", label: "Priority" },
43
+ { value: "-createdAt", label: "Newest" },
44
+ { value: "question", label: "Question A–Z" },
45
+ ],
46
+ columns: COLUMNS,
73
47
  mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
74
48
  id: toStringValue(item.id, `faq-${index}`),
75
49
  primary: toStringValue(item.question, "Untitled question"),
76
50
  secondary: toStringValue(item.category, "Uncategorized"),
77
- status: toStringValue(typeof item.isActive === "boolean" ? (item.isActive ? "Published" : "Draft") : item.status, "Draft"),
51
+ status: toStringValue(typeof item.isActive === "boolean"
52
+ ? item.isActive
53
+ ? "Published"
54
+ : "Draft"
55
+ : item.status, "Draft"),
78
56
  updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
79
57
  })),
80
58
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
81
- });
82
- const currentPage = table.getNumber("page", 1);
83
- const totalPages = Math.ceil(total / PAGE_SIZE);
84
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
85
- const bulkActions = [
86
- ...(onBulkArchive ? [{
87
- id: "bulk-archive",
88
- label: ACTIONS.ADMIN["archive-faq"].label,
89
- onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); refetch?.(); },
90
- }] : []),
91
- ...(onBulkDelete ? [{
92
- id: "bulk-delete",
93
- label: ACTIONS.ADMIN["delete-faq"].label,
94
- variant: "danger",
95
- onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); refetch?.(); },
96
- }] : []),
97
- ];
98
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
99
- if (hasChildren) {
100
- return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
59
+ buildFilters: (state) => state.isActive ? `isActive==${state.isActive}` : undefined,
60
+ primaryAction: {
61
+ label: "Add FAQ",
62
+ onClick: ({ openCreatePanel }) => openCreatePanel(),
63
+ },
64
+ buildBulkActions: (selection) => [
65
+ ...(onBulkArchive
66
+ ? [
67
+ buildBulkAction(ACTIONS.ADMIN["archive-faq"], async () => {
68
+ await onBulkArchive(selection.selectedIds);
69
+ selection.clearSelection();
70
+ }),
71
+ ]
72
+ : []),
73
+ ...(onBulkDelete
74
+ ? [
75
+ buildBulkAction(ACTIONS.ADMIN["delete-faq"], async () => {
76
+ await onBulkDelete(selection.selectedIds);
77
+ selection.clearSelection();
78
+ }),
79
+ ]
80
+ : []),
81
+ ],
82
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
83
+ { label: "All", value: "" },
84
+ { label: "Published", value: "true" },
85
+ { label: "Draft", value: "false" },
86
+ ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
87
+ ? "bg-primary text-white border-primary"
88
+ : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] })),
89
+ renderEditor: ({ editId, closePanel }) => (_jsx(AdminFaqEditorView, { faqId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
90
+ resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add FAQ" : "Edit FAQ"),
91
+ }), [onBulkArchive, onBulkDelete]);
92
+ if (React.Children.count(children) > 0) {
93
+ return (_jsxs(ListingLayout, { portal: "admin", ...props, children: [_jsx(Heading, { level: 1, className: "sr-only", children: "FAQs" }), children] }));
101
94
  }
102
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "FAQs" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search questions, categories, or tokens", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add FAQ"] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No FAQs found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(FaqsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add FAQ" : "Edit FAQ", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminFaqEditorView, { faqId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
95
+ return (_jsxs(_Fragment, { children: [_jsx(Heading, { level: 1, className: "sr-only", children: "FAQs" }), _jsx(DataListingView, { config: config })] }));
103
96
  }