@mohasinac/appkit 2.7.59 → 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 (311) 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/seo/og-layout.d.ts +42 -0
  33. package/dist/_internal/server/features/seo/og-layout.js +69 -0
  34. package/dist/_internal/server/features/stores/og.js +8 -41
  35. package/dist/_internal/server/features/sublisting-categories/og.js +11 -42
  36. package/dist/client.d.ts +10 -6
  37. package/dist/client.js +7 -3
  38. package/dist/constants/index.d.ts +4 -0
  39. package/dist/constants/index.js +2 -0
  40. package/dist/constants/notification-types.d.ts +20 -0
  41. package/dist/constants/notification-types.js +37 -0
  42. package/dist/constants/pagination-presets.d.ts +39 -0
  43. package/dist/constants/pagination-presets.js +38 -0
  44. package/dist/errors/messages.d.ts +0 -4
  45. package/dist/errors/messages.js +0 -4
  46. package/dist/features/account/components/UserOffersPanel.js +1 -1
  47. package/dist/features/account/components/UserSupportView.d.ts +1 -2
  48. package/dist/features/account/components/UserSupportView.js +9 -1
  49. package/dist/features/admin/actions/admin-actions.js +16 -1
  50. package/dist/features/admin/components/AdminAllEventEntriesView.d.ts +2 -3
  51. package/dist/features/admin/components/AdminAllEventEntriesView.js +50 -83
  52. package/dist/features/admin/components/AdminBidsView.d.ts +2 -3
  53. package/dist/features/admin/components/AdminBidsView.js +64 -98
  54. package/dist/features/admin/components/AdminBlogView.d.ts +3 -7
  55. package/dist/features/admin/components/AdminBlogView.js +53 -83
  56. package/dist/features/admin/components/AdminBrandsView.d.ts +2 -3
  57. package/dist/features/admin/components/AdminBrandsView.js +84 -83
  58. package/dist/features/admin/components/AdminBundlesView.js +82 -125
  59. package/dist/features/admin/components/AdminCarouselView.d.ts +2 -2
  60. package/dist/features/admin/components/AdminCarouselView.js +17 -52
  61. package/dist/features/admin/components/AdminCartsView.d.ts +2 -3
  62. package/dist/features/admin/components/AdminCartsView.js +39 -88
  63. package/dist/features/admin/components/AdminCategoriesView.d.ts +3 -3
  64. package/dist/features/admin/components/AdminCategoriesView.js +98 -92
  65. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  66. package/dist/features/admin/components/AdminClassifiedView.d.ts +3 -0
  67. package/dist/features/admin/components/AdminClassifiedView.js +44 -0
  68. package/dist/features/admin/components/AdminContactView.d.ts +2 -2
  69. package/dist/features/admin/components/AdminContactView.js +104 -111
  70. package/dist/features/admin/components/AdminCouponsView.d.ts +3 -6
  71. package/dist/features/admin/components/AdminCouponsView.js +66 -86
  72. package/dist/features/admin/components/AdminDigitalCodesView.d.ts +3 -0
  73. package/dist/features/admin/components/AdminDigitalCodesView.js +51 -0
  74. package/dist/features/admin/components/AdminFaqsView.d.ts +4 -4
  75. package/dist/features/admin/components/AdminFaqsView.js +77 -33
  76. package/dist/features/admin/components/AdminFeaturesView.d.ts +2 -3
  77. package/dist/features/admin/components/AdminFeaturesView.js +57 -70
  78. package/dist/features/admin/components/AdminGroupedListingsView.d.ts +3 -0
  79. package/dist/features/admin/components/AdminGroupedListingsView.js +43 -0
  80. package/dist/features/admin/components/AdminHistoryView.d.ts +2 -3
  81. package/dist/features/admin/components/AdminHistoryView.js +35 -52
  82. package/dist/features/admin/components/AdminListingScaffold.d.ts +2 -2
  83. package/dist/features/admin/components/AdminListingScaffold.js +3 -3
  84. package/dist/features/admin/components/AdminLiveView.d.ts +3 -0
  85. package/dist/features/admin/components/AdminLiveView.js +50 -0
  86. package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -2
  87. package/dist/features/admin/components/AdminNewsletterView.js +67 -97
  88. package/dist/features/admin/components/AdminNotificationsView.d.ts +2 -3
  89. package/dist/features/admin/components/AdminNotificationsView.js +67 -93
  90. package/dist/features/admin/components/AdminOrderEditorView.js +10 -10
  91. package/dist/features/admin/components/AdminOrdersView.d.ts +2 -3
  92. package/dist/features/admin/components/AdminOrdersView.js +93 -107
  93. package/dist/features/admin/components/AdminPayoutsView.d.ts +2 -3
  94. package/dist/features/admin/components/AdminPayoutsView.js +51 -91
  95. package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +2 -3
  96. package/dist/features/admin/components/AdminPrizeDrawsView.js +39 -76
  97. package/dist/features/admin/components/AdminProductEditorView.js +3 -3
  98. package/dist/features/admin/components/AdminProductsView.d.ts +4 -4
  99. package/dist/features/admin/components/AdminProductsView.js +167 -163
  100. package/dist/features/admin/components/AdminReturnRequestsView.d.ts +1 -1
  101. package/dist/features/admin/components/AdminReturnRequestsView.js +64 -66
  102. package/dist/features/admin/components/AdminReviewsView.d.ts +3 -7
  103. package/dist/features/admin/components/AdminReviewsView.js +94 -110
  104. package/dist/features/admin/components/AdminScammersView.d.ts +2 -3
  105. package/dist/features/admin/components/AdminScammersView.js +53 -97
  106. package/dist/features/admin/components/AdminSectionsView.js +1 -1
  107. package/dist/features/admin/components/AdminSessionsView.d.ts +2 -3
  108. package/dist/features/admin/components/AdminSessionsView.js +62 -89
  109. package/dist/features/admin/components/AdminStoreAddressesView.d.ts +1 -1
  110. package/dist/features/admin/components/AdminStoreAddressesView.js +39 -53
  111. package/dist/features/admin/components/AdminStoreEditorView.js +21 -8
  112. package/dist/features/admin/components/AdminStoresView.d.ts +2 -3
  113. package/dist/features/admin/components/AdminStoresView.js +67 -99
  114. package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +1 -1
  115. package/dist/features/admin/components/AdminSublistingCategoriesView.js +28 -44
  116. package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +10 -1
  117. package/dist/features/admin/components/AdminSupportTicketDetailView.js +191 -5
  118. package/dist/features/admin/components/AdminSupportTicketsView.d.ts +2 -3
  119. package/dist/features/admin/components/AdminSupportTicketsView.js +65 -96
  120. package/dist/features/admin/components/AdminTeamView.d.ts +2 -2
  121. package/dist/features/admin/components/AdminTeamView.js +40 -74
  122. package/dist/features/admin/components/AdminUserEditorView.d.ts +11 -1
  123. package/dist/features/admin/components/AdminUserEditorView.js +80 -6
  124. package/dist/features/admin/components/AdminUsersView.d.ts +2 -3
  125. package/dist/features/admin/components/AdminUsersView.js +108 -100
  126. package/dist/features/admin/components/AdminViewCards.d.ts +1 -1
  127. package/dist/features/admin/components/AdminWishlistsView.d.ts +2 -3
  128. package/dist/features/admin/components/AdminWishlistsView.js +36 -62
  129. package/dist/features/admin/components/DataListingView.d.ts +122 -0
  130. package/dist/features/admin/components/DataListingView.js +66 -0
  131. package/dist/features/admin/components/index.d.ts +10 -4
  132. package/dist/features/admin/components/index.js +6 -2
  133. package/dist/features/admin/constants/filter-tabs.d.ts +23 -17
  134. package/dist/features/admin/constants/filter-tabs.js +21 -17
  135. package/dist/features/admin/hooks/useAdminListing.d.ts +1 -0
  136. package/dist/features/admin/hooks/useAdminListing.js +1 -1
  137. package/dist/features/admin/schemas/firestore.js +6 -7
  138. package/dist/features/auctions/server.d.ts +0 -1
  139. package/dist/features/auctions/server.js +0 -1
  140. package/dist/features/auth/actions/realtime-token-actions.js +1 -1
  141. package/dist/features/auth/repository/user.repository.js +1 -1
  142. package/dist/features/cart/repository/cart.repository.d.ts +4 -1
  143. package/dist/features/cart/repository/cart.repository.js +12 -0
  144. package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -2
  145. package/dist/features/checkout/index.d.ts +1 -1
  146. package/dist/features/events/components/AdminEventsView.d.ts +3 -3
  147. package/dist/features/events/components/AdminEventsView.js +50 -86
  148. package/dist/features/events/components/EventOfferCard.d.ts +19 -0
  149. package/dist/features/events/components/EventOfferCard.js +29 -0
  150. package/dist/features/events/components/EventRaffleEntryForm.d.ts +25 -0
  151. package/dist/features/events/components/EventRaffleEntryForm.js +37 -0
  152. package/dist/features/events/components/EventRafflesSection.d.ts +1 -4
  153. package/dist/features/events/components/EventRafflesSection.js +17 -6
  154. package/dist/features/events/components/EventSaleBanner.d.ts +21 -0
  155. package/dist/features/events/components/EventSaleBanner.js +18 -0
  156. package/dist/features/events/components/index.d.ts +6 -0
  157. package/dist/features/events/components/index.js +4 -0
  158. package/dist/features/events/repository/event-entry.repository.d.ts +5 -0
  159. package/dist/features/events/repository/event-entry.repository.js +7 -0
  160. package/dist/features/filters/AsyncFacetSection.d.ts +1 -1
  161. package/dist/features/homepage/components/FAQSection.d.ts +0 -5
  162. package/dist/features/homepage/lib/live-stats.js +1 -1
  163. package/dist/features/homepage/schemas/firestore.d.ts +0 -2
  164. package/dist/features/layout/index.d.ts +0 -2
  165. package/dist/features/layout/index.js +0 -1
  166. package/dist/features/pre-orders/server.d.ts +0 -1
  167. package/dist/features/pre-orders/server.js +0 -1
  168. package/dist/features/products/components/GroupSettingsPanel.js +2 -2
  169. package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
  170. package/dist/features/products/components/PrizeDrawsSection.d.ts +1 -4
  171. package/dist/features/products/components/PrizeDrawsSection.js +17 -6
  172. package/dist/features/products/components/PrizeRevealModal.js +1 -1
  173. package/dist/features/products/components/ProductCardMetadataSection.d.ts +11 -0
  174. package/dist/features/products/components/ProductCardMetadataSection.js +25 -0
  175. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  176. package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
  177. package/dist/features/products/components/ProductGradingTab.d.ts +13 -0
  178. package/dist/features/products/components/ProductGradingTab.js +25 -0
  179. package/dist/features/products/components/ProductGrid.js +14 -2
  180. package/dist/features/products/components/ProductTabsShell.d.ts +5 -1
  181. package/dist/features/products/components/ProductTabsShell.js +6 -1
  182. package/dist/features/products/components/index.d.ts +4 -0
  183. package/dist/features/products/components/index.js +3 -0
  184. package/dist/features/products/constants/action-defs.d.ts +5 -0
  185. package/dist/features/products/constants/action-defs.js +52 -32
  186. package/dist/features/products/constants/sieve.d.ts +24 -4
  187. package/dist/features/products/constants/sieve.js +20 -11
  188. package/dist/features/products/schemas/firestore.d.ts +17 -0
  189. package/dist/features/products/schemas/product-features.validators.d.ts +2 -2
  190. package/dist/features/products/schemas/product-templates.d.ts +2 -4
  191. package/dist/features/products/types/index.d.ts +4 -0
  192. package/dist/features/promotions/server.d.ts +0 -1
  193. package/dist/features/promotions/server.js +0 -1
  194. package/dist/features/search/api/route.js +44 -1
  195. package/dist/features/search/components/Search.js +2 -0
  196. package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
  197. package/dist/features/search/hooks/useNavSuggestions.js +31 -4
  198. package/dist/features/seller/components/BrandInlineSelect.js +2 -2
  199. package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
  200. package/dist/features/seller/components/SellerAddressesView.js +1 -1
  201. package/dist/features/seller/components/SellerAuctionsView.d.ts +3 -3
  202. package/dist/features/seller/components/SellerAuctionsView.js +60 -90
  203. package/dist/features/seller/components/SellerBidsView.js +1 -1
  204. package/dist/features/seller/components/SellerBundlesView.js +64 -89
  205. package/dist/features/seller/components/SellerClassifiedView.js +67 -90
  206. package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
  207. package/dist/features/seller/components/SellerCouponsView.d.ts +2 -2
  208. package/dist/features/seller/components/SellerCouponsView.js +45 -84
  209. package/dist/features/seller/components/SellerDigitalCodesView.js +63 -88
  210. package/dist/features/seller/components/SellerGroupedListingsView.js +33 -49
  211. package/dist/features/seller/components/SellerLiveView.js +63 -84
  212. package/dist/features/seller/components/SellerOffersPanel.js +1 -1
  213. package/dist/features/seller/components/SellerOffersView.d.ts +2 -2
  214. package/dist/features/seller/components/SellerOffersView.js +37 -69
  215. package/dist/features/seller/components/SellerOrdersView.d.ts +2 -2
  216. package/dist/features/seller/components/SellerOrdersView.js +4 -4
  217. package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
  218. package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -2
  219. package/dist/features/seller/components/SellerPayoutsView.js +34 -68
  220. package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -2
  221. package/dist/features/seller/components/SellerPreOrdersView.js +49 -82
  222. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -2
  223. package/dist/features/seller/components/SellerPrizeDrawsView.js +49 -82
  224. package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
  225. package/dist/features/seller/components/SellerProductsView.d.ts +2 -2
  226. package/dist/features/seller/components/SellerProductsView.js +15 -10
  227. package/dist/features/seller/components/SellerShippingConfigsView.js +1 -1
  228. package/dist/features/seller/components/SellerSidebar.d.ts +0 -4
  229. package/dist/features/seller/components/SellerSidebar.js +0 -2
  230. package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
  231. package/dist/features/seller/components/SellerTemplatesView.js +2 -2
  232. package/dist/features/seller/components/SellersListView.d.ts +1 -2
  233. package/dist/features/seller/components/analytics/SellerAnalyticsStats.d.ts +5 -0
  234. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +2 -8
  235. package/dist/features/seller/components/index.d.ts +0 -3
  236. package/dist/features/seller/components/index.js +0 -2
  237. package/dist/features/seller/server.d.ts +0 -1
  238. package/dist/features/seller/server.js +0 -1
  239. package/dist/features/shell/FormShell.d.ts +1 -1
  240. package/dist/features/shell/FormShell.js +4 -1
  241. package/dist/features/shell/field-groups/ImageFieldGroup.d.ts +18 -0
  242. package/dist/features/shell/field-groups/ImageFieldGroup.js +15 -0
  243. package/dist/features/shell/field-groups/SeoFieldGroup.d.ts +22 -0
  244. package/dist/features/shell/field-groups/SeoFieldGroup.js +16 -0
  245. package/dist/features/shell/field-groups/StatusFieldGroup.d.ts +35 -0
  246. package/dist/features/shell/field-groups/StatusFieldGroup.js +16 -0
  247. package/dist/features/shell/field-groups/TitleDescriptionGroup.d.ts +40 -0
  248. package/dist/features/shell/field-groups/TitleDescriptionGroup.js +16 -0
  249. package/dist/features/shell/field-groups/index.d.ts +13 -0
  250. package/dist/features/shell/field-groups/index.js +9 -0
  251. package/dist/features/shell/index.d.ts +2 -0
  252. package/dist/features/shell/index.js +1 -0
  253. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +1 -2
  254. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +1 -2
  255. package/dist/features/stores/components/StoreHeader.js +2 -1
  256. package/dist/features/stores/components/StoreListingsGuideView.d.ts +1 -2
  257. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +1 -2
  258. package/dist/features/stores/components/StoreScopedSearch.d.ts +16 -0
  259. package/dist/features/stores/components/StoreScopedSearch.js +27 -0
  260. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +1 -2
  261. package/dist/features/stores/server.d.ts +0 -1
  262. package/dist/features/stores/server.js +0 -1
  263. package/dist/features/support/schemas/firestore.d.ts +22 -1
  264. package/dist/features/support/schemas/firestore.js +10 -0
  265. package/dist/features/wishlist/server.d.ts +0 -1
  266. package/dist/features/wishlist/server.js +0 -1
  267. package/dist/index.d.ts +9 -27
  268. package/dist/index.js +14 -50
  269. package/dist/next/routing/route-map.d.ts +8 -0
  270. package/dist/next/routing/route-map.js +4 -0
  271. package/dist/react/hooks/useListingTypeFlags.d.ts +32 -0
  272. package/dist/react/hooks/useListingTypeFlags.js +19 -0
  273. package/dist/security/index.d.ts +1 -1
  274. package/dist/security/index.js +1 -1
  275. package/dist/security/pii-redact.d.ts +8 -0
  276. package/dist/security/pii-redact.js +10 -1
  277. package/dist/seed/grouped-listings-seed-data.d.ts +4 -0
  278. package/dist/seed/products-preorders-seed-data.d.ts +4 -0
  279. package/dist/seed/products-prize-draws-seed-data.js +3 -0
  280. package/dist/seed/site-settings-seed-data.js +5 -5
  281. package/dist/seed/sub-listings-seed-data.d.ts +4 -0
  282. package/dist/server.d.ts +2 -0
  283. package/dist/server.js +4 -0
  284. package/dist/styles.css +54 -395
  285. package/dist/tailwind-utilities.css +1 -1
  286. package/dist/ui/components/BaseListingCard.d.ts +10 -0
  287. package/dist/ui/components/BaseListingCard.js +17 -1
  288. package/dist/ui/components/DetailPageGallery.d.ts +32 -0
  289. package/dist/ui/components/DetailPageGallery.js +47 -0
  290. package/dist/ui/components/DetailPageHero.d.ts +30 -0
  291. package/dist/ui/components/DetailPageHero.js +22 -0
  292. package/dist/ui/components/DetailPageTabs.d.ts +34 -0
  293. package/dist/ui/components/DetailPageTabs.js +38 -0
  294. package/dist/ui/components/HorizontalScroller.style.css +15 -2
  295. package/dist/ui/components/ListingLayout.d.ts +11 -2
  296. package/dist/ui/components/ListingLayout.js +12 -6
  297. package/dist/ui/components/PaginatedSelect.d.ts +66 -0
  298. package/dist/ui/components/PaginatedSelect.js +201 -0
  299. package/dist/ui/components/PaginatedSelect.style.css +254 -0
  300. package/dist/ui/components/Semantic.d.ts +0 -4
  301. package/dist/ui/components/Semantic.js +0 -2
  302. package/dist/ui/components/SettingsSection.d.ts +47 -0
  303. package/dist/ui/components/SettingsSection.js +38 -0
  304. package/dist/ui/components/index.style.css +1 -4
  305. package/dist/ui/index.d.ts +12 -14
  306. package/dist/ui/index.js +6 -7
  307. package/dist/utils/id-generators.d.ts +1 -18
  308. package/dist/utils/id-generators.js +51 -18
  309. package/dist/validation/schemas.d.ts +25 -0
  310. package/dist/validation/schemas.js +28 -0
  311. package/package.json +1 -1
@@ -1,42 +1,26 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
4
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
- import { BulkActionBar, ListingToolbar, Pagination } from "../../../ui";
7
- import { useBottomActions } from "../../layout";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
8
3
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
4
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
10
5
  import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
11
- import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
12
- import { DataTable } from "./DataTable";
13
- const PAGE_SIZE = 25;
14
- const DEFAULT_SORT = "name";
15
- const SORT_OPTIONS = [
16
- { value: "name", label: "Name A–Z" },
17
- { value: "-name", label: "Name Z–A" },
18
- { value: "-productCount", label: "Most listings" },
19
- { value: "-createdAt", label: "Newest" },
20
- ];
21
- export function AdminSublistingCategoriesView({ onBulkDelete }) {
22
- const [view, setView] = useState("table");
23
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
24
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
25
- const commitSearch = useCallback(() => {
26
- table.set("q", searchInput.trim());
27
- }, [searchInput, table]);
28
- const resetAll = useCallback(() => {
29
- table.setMany({ q: "", sort: "" });
30
- setSearchInput("");
31
- }, [table]);
32
- const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT;
33
- const { rows, total, isLoading, errorMessage } = useAdminListingData({
6
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
7
+ import { DataListingView } from "./DataListingView";
8
+ export function AdminSublistingCategoriesView({ onBulkDelete, }) {
9
+ const config = {
10
+ portal: "admin",
11
+ title: "Sub-listing Categories",
12
+ searchPlaceholder: "Search sub-listing categories…",
13
+ emptyLabel: "No sub-listing categories found",
14
+ filterKeys: [],
15
+ defaultSort: "name",
34
16
  queryKey: ["admin", "sublisting-categories", "listing"],
35
17
  endpoint: ADMIN_ENDPOINTS.SUBLISTING_CATEGORIES,
36
- page: table.getNumber("page", 1),
37
- pageSize: PAGE_SIZE,
38
- sorts: table.get("sort") || DEFAULT_SORT,
39
- q: table.get("q") || undefined,
18
+ sortOptions: [
19
+ { value: "name", label: "Name A–Z" },
20
+ { value: "-name", label: "Name Z–A" },
21
+ { value: "-productCount", label: "Most listings" },
22
+ { value: "-createdAt", label: "Newest" },
23
+ ],
40
24
  mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
41
25
  id: toStringValue(item.id, `sc-${index}`),
42
26
  primary: toStringValue(item.name, "Untitled"),
@@ -50,15 +34,15 @@ export function AdminSublistingCategoriesView({ onBulkDelete }) {
50
34
  updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
51
35
  })),
52
36
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
53
- });
54
- const currentPage = table.getNumber("page", 1);
55
- const totalPages = Math.ceil(total / PAGE_SIZE);
56
- const selection = useBulkSelection({ items: rows ?? [], keyExtractor: (r) => r.id });
57
- const bulkActions = [
58
- ...(onBulkDelete ? [buildBulkAction(ACTIONS.ADMIN["delete-sublisting-category"], async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); })] : []),
59
- ];
60
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
61
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { searchValue: searchInput, searchPlaceholder: "Search sub-listing categories\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
62
- table.set("sort", v);
63
- }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), 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 })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sub-listing categories found" })] })] }));
37
+ buildFilters: () => undefined,
38
+ buildBulkActions: onBulkDelete
39
+ ? (selection) => [
40
+ buildBulkAction(ACTIONS.ADMIN["delete-sublisting-category"], async () => {
41
+ await onBulkDelete(selection.selectedIds);
42
+ selection.clearSelection();
43
+ }),
44
+ ]
45
+ : undefined,
46
+ };
47
+ return _jsx(DataListingView, { config: config });
64
48
  }
@@ -5,6 +5,13 @@ interface TicketMessageClient {
5
5
  body?: string;
6
6
  createdAt?: string;
7
7
  }
8
+ interface RelatedPartiesClient {
9
+ userId?: string;
10
+ storeId?: string;
11
+ orderId?: string;
12
+ productId?: string;
13
+ bidId?: string;
14
+ }
8
15
  export interface AdminSupportTicketDetailViewProps {
9
16
  open: boolean;
10
17
  onClose: () => void;
@@ -18,6 +25,8 @@ export interface AdminSupportTicketDetailViewProps {
18
25
  messages?: TicketMessageClient[];
19
26
  internalNotes?: string;
20
27
  orderId?: string;
28
+ /** ST-6 — current relatedParties snapshot from the ticket document. */
29
+ relatedParties?: RelatedPartiesClient;
21
30
  }
22
- export declare function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages, internalNotes, orderId, }: AdminSupportTicketDetailViewProps): import("react/jsx-runtime").JSX.Element;
31
+ export declare function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages, internalNotes, orderId, relatedParties, }: AdminSupportTicketDetailViewProps): import("react/jsx-runtime").JSX.Element;
23
32
  export {};
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React from "react";
4
4
  import { useMutation, useQueryClient } from "@tanstack/react-query";
5
- import { Button, Div, FormActions, Select, SideDrawer, Text, useToast, } from "../../../ui";
5
+ import { Button, Div, FormActions, Input, Row, Select, SideDrawer, Text, Toggle, useToast, } from "../../../ui";
6
6
  import { apiClient } from "../../../http";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  const STATUS_OPTIONS = [
@@ -30,28 +30,46 @@ const ROLE_LABEL = {
30
30
  support: "Support",
31
31
  admin: "Admin",
32
32
  };
33
- export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages = [], internalNotes, orderId, }) {
33
+ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages = [], internalNotes, orderId, relatedParties, }) {
34
34
  const queryClient = useQueryClient();
35
35
  const { showToast } = useToast();
36
36
  const [status, setStatus] = React.useState(currentStatus ?? "open");
37
37
  const [priority, setPriority] = React.useState(currentPriority ?? "normal");
38
38
  const [notes, setNotes] = React.useState(internalNotes ?? "");
39
39
  const [replyBody, setReplyBody] = React.useState("");
40
+ // ST-6 — local edits for the linked-parties panel
41
+ const [parties, setParties] = React.useState(relatedParties ?? {});
40
42
  React.useEffect(() => {
41
43
  if (open) {
42
44
  setStatus(currentStatus ?? "open");
43
45
  setPriority(currentPriority ?? "normal");
44
46
  setNotes(internalNotes ?? "");
45
47
  setReplyBody("");
48
+ setParties(relatedParties ?? {});
46
49
  }
47
- }, [open, currentStatus, currentPriority, internalNotes]);
50
+ }, [open, currentStatus, currentPriority, internalNotes, relatedParties]);
48
51
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ["admin", "support-tickets"] });
52
+ const cleanedParties = React.useMemo(() => {
53
+ const out = {};
54
+ if (parties.userId?.trim())
55
+ out.userId = parties.userId.trim();
56
+ if (parties.storeId?.trim())
57
+ out.storeId = parties.storeId.trim();
58
+ if (parties.orderId?.trim())
59
+ out.orderId = parties.orderId.trim();
60
+ if (parties.productId?.trim())
61
+ out.productId = parties.productId.trim();
62
+ if (parties.bidId?.trim())
63
+ out.bidId = parties.bidId.trim();
64
+ return out;
65
+ }, [parties]);
49
66
  const updateMutation = useMutation({
50
67
  mutationFn: async () => {
51
68
  await apiClient.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
52
69
  status,
53
70
  priority,
54
71
  internalNotes: notes || undefined,
72
+ relatedParties: Object.keys(cleanedParties).length > 0 ? cleanedParties : undefined,
55
73
  });
56
74
  },
57
75
  onSuccess: () => {
@@ -77,7 +95,175 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
77
95
  showToast(err?.message ?? "Failed to send reply.", "error");
78
96
  },
79
97
  });
98
+ // ST-4 — Apply Store Change action (only relevant for store_change_request)
99
+ const [storeStatus, setStoreStatus] = React.useState("active");
100
+ const [storeIsVerified, setStoreIsVerified] = React.useState(false);
101
+ const [storeIsFeatured, setStoreIsFeatured] = React.useState(false);
102
+ const linkedStoreId = parties.storeId?.trim();
103
+ const isStoreChangeRequest = category === "store_change_request";
104
+ const applyStoreChange = useMutation({
105
+ mutationFn: async () => {
106
+ if (!linkedStoreId)
107
+ throw new Error("No linked store on this ticket.");
108
+ await apiClient.patch(ADMIN_ENDPOINTS.STORE_BY_ID(linkedStoreId), {
109
+ storeStatus,
110
+ isVerified: storeIsVerified,
111
+ isFeatured: storeIsFeatured,
112
+ });
113
+ },
114
+ onSuccess: () => {
115
+ showToast("Store change applied.", "success");
116
+ // Append an internal note marking the action; non-fatal if it fails.
117
+ apiClient
118
+ .patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
119
+ internalNotes: (notes ? notes + "\n" : "") +
120
+ `[${new Date().toISOString()}] Applied store change to ${linkedStoreId}: status=${storeStatus}, verified=${storeIsVerified}, featured=${storeIsFeatured}`,
121
+ })
122
+ .catch(() => { });
123
+ invalidate();
124
+ },
125
+ onError: (err) => {
126
+ showToast(err?.message ?? "Failed to apply store change.", "error");
127
+ },
128
+ });
129
+ // ST-3 — Modify Order Items panel (fetches current items on demand, lets
130
+ // admin edit quantity per line + remove lines, then PATCHes the order).
131
+ const [orderItemsOpen, setOrderItemsOpen] = React.useState(false);
132
+ const [orderItems, setOrderItems] = React.useState([]);
133
+ const linkedOrderId = parties.orderId?.trim() || orderId;
134
+ const isOrderModificationRequest = category === "order_modification_request" || category === "order_issue";
135
+ const loadOrderItems = useMutation({
136
+ mutationFn: async () => {
137
+ if (!linkedOrderId)
138
+ throw new Error("No linked order on this ticket.");
139
+ const res = await apiClient.get(ADMIN_ENDPOINTS.ORDER_BY_ID(linkedOrderId));
140
+ const items = Array.isArray(res?.items) ? res.items : [];
141
+ setOrderItems(items.map((it) => ({
142
+ productId: String(it.productId ?? ""),
143
+ productTitle: String(it.productTitle ?? ""),
144
+ quantity: Number(it.quantity ?? 0),
145
+ unitPrice: Number(it.unitPrice ?? 0),
146
+ totalPrice: Number(it.totalPrice ?? 0),
147
+ })));
148
+ setOrderItemsOpen(true);
149
+ },
150
+ onError: (err) => {
151
+ showToast(err?.message ?? "Failed to load order.", "error");
152
+ },
153
+ });
154
+ // ST-5 — Lift Ban actions for unban_request tickets
155
+ const linkedUserId = parties.userId?.trim();
156
+ const isUnbanRequest = category === "unban_request";
157
+ const liftHardBan = useMutation({
158
+ mutationFn: async () => {
159
+ if (!linkedUserId)
160
+ throw new Error("No linked user on this ticket.");
161
+ await apiClient.post(ADMIN_ENDPOINTS.USER_UNBAN(linkedUserId), {});
162
+ },
163
+ onSuccess: () => {
164
+ showToast("Hard ban lifted.", "success");
165
+ apiClient
166
+ .patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
167
+ internalNotes: (notes ? notes + "\n" : "") +
168
+ `[${new Date().toISOString()}] Lifted hard ban for ${linkedUserId}`,
169
+ status: "resolved",
170
+ })
171
+ .catch(() => { });
172
+ invalidate();
173
+ },
174
+ onError: (err) => {
175
+ showToast(err?.message ?? "Failed to lift hard ban.", "error");
176
+ },
177
+ });
178
+ const liftSoftBanTickets = useMutation({
179
+ mutationFn: async () => {
180
+ if (!linkedUserId)
181
+ throw new Error("No linked user on this ticket.");
182
+ await apiClient.delete(ADMIN_ENDPOINTS.USER_SOFT_BAN_LIFT(linkedUserId, "create_support_tickets"));
183
+ },
184
+ onSuccess: () => {
185
+ showToast("Ticket-creation soft ban lifted.", "success");
186
+ apiClient
187
+ .patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
188
+ internalNotes: (notes ? notes + "\n" : "") +
189
+ `[${new Date().toISOString()}] Lifted create_support_tickets soft ban for ${linkedUserId}`,
190
+ status: "resolved",
191
+ })
192
+ .catch(() => { });
193
+ invalidate();
194
+ },
195
+ onError: (err) => {
196
+ showToast(err?.message ?? "Failed to lift soft ban.", "error");
197
+ },
198
+ });
199
+ const applyOrderItems = useMutation({
200
+ mutationFn: async () => {
201
+ if (!linkedOrderId)
202
+ throw new Error("No linked order on this ticket.");
203
+ // Recalculate totalPrice per line from quantity × unitPrice so qty edits
204
+ // flow through. Lines with quantity 0 are dropped.
205
+ const items = orderItems
206
+ .filter((it) => it.quantity > 0)
207
+ .map((it) => ({
208
+ productId: it.productId,
209
+ productTitle: it.productTitle,
210
+ quantity: it.quantity,
211
+ unitPrice: it.unitPrice,
212
+ totalPrice: it.quantity * it.unitPrice,
213
+ }));
214
+ await apiClient.patch(ADMIN_ENDPOINTS.ORDER_BY_ID(linkedOrderId), {
215
+ items,
216
+ });
217
+ },
218
+ onSuccess: () => {
219
+ showToast("Order items updated.", "success");
220
+ apiClient
221
+ .patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
222
+ internalNotes: (notes ? notes + "\n" : "") +
223
+ `[${new Date().toISOString()}] Modified order ${linkedOrderId} items: ${orderItems
224
+ .filter((it) => it.quantity > 0)
225
+ .map((it) => `${it.productId}x${it.quantity}`)
226
+ .join(", ")}`,
227
+ })
228
+ .catch(() => { });
229
+ invalidate();
230
+ setOrderItemsOpen(false);
231
+ },
232
+ onError: (err) => {
233
+ showToast(err?.message ?? "Failed to update order items.", "error");
234
+ },
235
+ });
80
236
  return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: subject ?? "Support Ticket", children: _jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_COLOR[currentStatus ?? "open"] ?? STATUS_COLOR.open}`, children: (currentStatus ?? "open").replace(/_/g, " ") }), _jsx("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: category }), orderId && (_jsxs("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: ["Order: ", orderId] }))] }), _jsxs(Div, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: ["From: ", _jsx("span", { className: "font-medium text-zinc-700 dark:text-zinc-200", children: userDisplayName })] }), description && (_jsxs(Div, { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsx(Text, { className: "mb-1 text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Description" }), _jsx(Text, { className: "whitespace-pre-wrap text-sm text-zinc-700 dark:text-zinc-200", children: description })] })), messages.length > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: ["Messages (", messages.length, ")"] }), _jsx("div", { className: "space-y-2 max-h-64 overflow-y-auto", children: messages.map((msg, i) => (_jsxs("div", { className: `rounded-lg p-3 text-sm ${msg.authorRole === "user"
81
237
  ? "bg-zinc-50 border border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-700"
82
- : "bg-info-surface border border-blue-200 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Reply to user" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Type a reply\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || !ticketId || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }), _jsx("hr", { className: "border-zinc-200 dark:border-zinc-700" }), _jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Select, { label: "Priority", options: PRIORITY_OPTIONS, value: priority, onValueChange: setPriority }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Internal notes (staff only)" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Notes visible only to admins and employees\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
238
+ : "bg-info-surface border border-blue-200 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Reply to user" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Type a reply\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || !ticketId || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }), _jsx("hr", { className: "border-zinc-200 dark:border-zinc-700" }), _jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Select, { label: "Priority", options: PRIORITY_OPTIONS, value: priority, onValueChange: setPriority }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Internal notes (staff only)" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Notes visible only to admins and employees\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-zinc-200 dark:border-zinc-700", children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Linked parties" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Tag the buyer / store / order / product / bid this ticket concerns." }), [
239
+ ["userId", "User slug"],
240
+ ["storeId", "Store slug"],
241
+ ["orderId", "Order ID"],
242
+ ["productId", "Product slug"],
243
+ ["bidId", "Bid ID"],
244
+ ].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
245
+ }
246
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
+ function OrderItemsPanel(props) {
248
+ const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
249
+ const updateQty = (idx, raw) => {
250
+ const q = Math.max(0, parseInt(raw, 10) || 0);
251
+ setOrderItems((prev) => prev.map((p, i) => (i === idx ? { ...p, quantity: q } : p)));
252
+ };
253
+ return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-info/40 bg-info-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-info uppercase tracking-wide", children: "Modify order items" }), linkedOrderId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing order: ", _jsx("code", { className: "font-mono", children: linkedOrderId })] }), !orderItemsOpen ? (_jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: loadOrderItems.isPending, disabled: loadOrderItems.isPending, onClick: () => loadOrderItems.mutate(), children: "Load current items" })) : (_jsxs(_Fragment, { children: [orderItems.length === 0 ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Order has no items." })) : (_jsx(Div, { className: "flex flex-col gap-2", children: orderItems.map((it, idx) => (_jsxs(Div, { className: "flex items-center gap-2 rounded border border-zinc-200 dark:border-zinc-700 px-2 py-1", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-xs font-medium truncate", children: it.productTitle }), _jsxs(Text, { size: "xs", color: "muted", children: [it.productId, " \u00B7 \u20B9", (it.unitPrice / 100).toFixed(2), "/ea"] })] }), _jsx("input", { type: "number", min: "0", value: it.quantity, onChange: (e) => updateQty(idx, e.target.value), className: "w-16 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-2 py-1 text-xs" })] }, `${it.productId}-${idx}`))) })), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOrderItemsOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyOrderItems.isPending, disabled: applyOrderItems.isPending, onClick: () => applyOrderItems.mutate(), children: "Apply changes" })] })] }))] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Order ID" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
254
+ }
255
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
256
+ function StoreChangePanel(props) {
257
+ const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
258
+ return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-warning/40 bg-warning-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-warning uppercase tracking-wide", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing store: ", _jsx("code", { className: "font-mono", children: linkedStoreId })] }), _jsx(Select, { label: "Store status", options: [
259
+ { label: "Active", value: "active" },
260
+ { label: "Pending", value: "pending" },
261
+ { label: "Suspended", value: "suspended" },
262
+ { label: "Rejected", value: "rejected" },
263
+ ], value: storeStatus, onValueChange: setStoreStatus }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
264
+ }
265
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
266
+ function UnbanRequestPanel(props) {
267
+ const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
268
+ return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-error/40 bg-error-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-error uppercase tracking-wide", children: "Lift account ban" }), linkedUserId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Appellant: ", _jsx("code", { className: "font-mono", children: linkedUserId })] }), _jsx(Text, { size: "xs", color: "muted", children: "Resolves the ticket and appends an audit note on success." }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: liftHardBan.isPending, disabled: liftHardBan.isPending, onClick: () => liftHardBan.mutate(), children: "Lift hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftSoftBanTickets.isPending, disabled: liftSoftBanTickets.isPending, onClick: () => liftSoftBanTickets.mutate(), children: "Lift ticket soft ban" })] })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "User slug" }), " ", "field in the Linked parties panel above to enable these actions."] }))] }));
83
269
  }
@@ -1,4 +1,3 @@
1
- import type { ListingViewShellProps } from "../../../ui";
2
- export interface AdminSupportTicketsViewProps extends ListingViewShellProps {
3
- }
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export type AdminSupportTicketsViewProps = ListingLayoutProps;
4
3
  export declare function AdminSupportTicketsView({ children, ...props }: AdminSupportTicketsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -1,23 +1,13 @@
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 { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
6
- import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, ListingFilterDrawer } from "../../../ui";
3
+ import React, { useState } from "react";
4
+ import { FilterChipGroup, ListingLayout, RowActionMenu, Text } from "../../../ui";
7
5
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
6
  import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
9
7
  import { ADMIN_SUPPORT_TICKET_STATUS_TABS, ADMIN_SUPPORT_TICKET_PRIORITY_TABS, } from "../constants/filter-tabs";
10
- import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
11
- import { DataTable } from "./DataTable";
8
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
9
+ import { DataListingView } from "./DataListingView";
12
10
  import { AdminSupportTicketDetailView } from "./AdminSupportTicketDetailView";
13
- const PAGE_SIZE = 25;
14
- const FILTER_KEYS = ["status", "priority"];
15
- const DEFAULT_SORT = "-createdAt";
16
- const SORT_OPTIONS = [
17
- { value: "-createdAt", label: "Newest" },
18
- { value: "createdAt", label: "Oldest" },
19
- { value: "-updatedAt", label: "Recently updated" },
20
- ];
21
11
  const PRIORITY_BADGE = {
22
12
  urgent: "bg-error-surface text-error",
23
13
  high: "bg-warning-surface text-warning",
@@ -31,81 +21,49 @@ const STATUS_BADGE = {
31
21
  resolved: "bg-success-surface text-success",
32
22
  closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
33
23
  };
34
- function buildTicketColumns(setSelectedRow, setDrawerOpen) {
35
- return [
36
- {
37
- key: "primary",
38
- header: "Subject",
39
- render: (row) => {
40
- const priority = toStringValue(row._raw?.priority, "normal");
41
- return (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: row.primary }), row.secondary ? _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.secondary }) : null, _jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${PRIORITY_BADGE[priority] ?? PRIORITY_BADGE.normal}`, children: priority })] }));
42
- },
43
- },
44
- {
45
- key: "status",
46
- header: "Status",
47
- className: "w-36",
48
- render: (row) => (_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[row.status] ?? STATUS_BADGE.open}`, children: row.status.replace(/_/g, " ") })),
24
+ const TICKET_COLUMNS = [
25
+ {
26
+ key: "primary",
27
+ header: "Subject",
28
+ render: (row) => {
29
+ const priority = toStringValue(row._raw?.priority, "normal");
30
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: row.primary }), row.secondary ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.secondary })) : null, _jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${PRIORITY_BADGE[priority] ?? PRIORITY_BADGE.normal}`, children: priority })] }));
49
31
  },
50
- {
51
- key: "updatedAt",
52
- header: "Updated",
53
- className: "w-32",
54
- render: (row) => (_jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
55
- },
56
- ];
57
- }
32
+ },
33
+ {
34
+ key: "status",
35
+ header: "Status",
36
+ className: "w-36",
37
+ render: (row) => (_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[row.status] ?? STATUS_BADGE.open}`, children: row.status.replace(/_/g, " ") })),
38
+ },
39
+ {
40
+ key: "updatedAt",
41
+ header: "Updated",
42
+ className: "w-32",
43
+ render: (row) => (_jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
44
+ },
45
+ ];
58
46
  export function AdminSupportTicketsView({ children, ...props }) {
59
- const hasChildren = React.Children.count(children) > 0;
60
- const [view, setView] = useState("table");
61
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
62
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
63
- const [filterOpen, setFilterOpen] = useState(false);
64
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
65
47
  const [drawerOpen, setDrawerOpen] = useState(false);
66
48
  const [selectedRow, setSelectedRow] = useState(null);
67
- const openFilters = useCallback(() => {
68
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
69
- setFilterOpen(true);
70
- }, [table]);
71
- const applyFilters = useCallback(() => {
72
- const updates = { page: "1" };
73
- for (const k of FILTER_KEYS)
74
- updates[k] = pendingFilters[k] ?? "";
75
- table.setMany(updates);
76
- setFilterOpen(false);
77
- }, [pendingFilters, table]);
78
- const clearFilters = useCallback(() => {
79
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
80
- }, []);
81
- const resetAll = useCallback(() => {
82
- const updates = { q: "", sort: "" };
83
- for (const k of FILTER_KEYS)
84
- updates[k] = "";
85
- table.setMany(updates);
86
- setSearchInput("");
87
- }, [table]);
88
- const commitSearch = useCallback(() => {
89
- table.set("q", searchInput.trim());
90
- }, [searchInput, table]);
91
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
92
- const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
93
- const filterParts = [];
94
- const statusRaw = table.get("status");
95
- if (statusRaw && statusRaw !== "All")
96
- filterParts.push(`status==${statusRaw}`);
97
- const priorityRaw = table.get("priority");
98
- if (priorityRaw && priorityRaw !== "All")
99
- filterParts.push(`priority==${priorityRaw}`);
100
- const filters = filterParts.join(",") || undefined;
101
- const { rows, total, isLoading, errorMessage } = useAdminListingData({
49
+ if (React.Children.count(children) > 0) {
50
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
51
+ }
52
+ const config = {
53
+ portal: "admin",
54
+ title: "Support Tickets",
55
+ searchPlaceholder: "Search by subject",
56
+ emptyLabel: "No support tickets found",
57
+ filterKeys: ["status", "priority"],
58
+ defaultSort: "-createdAt",
102
59
  queryKey: ["admin", "support-tickets", "listing"],
103
60
  endpoint: ADMIN_ENDPOINTS.SUPPORT_TICKETS,
104
- page: table.getNumber("page", 1),
105
- pageSize: PAGE_SIZE,
106
- sorts: table.get("sort") || DEFAULT_SORT,
107
- filters,
108
- q: table.get("q") || undefined,
61
+ sortOptions: [
62
+ { value: "-createdAt", label: "Newest" },
63
+ { value: "createdAt", label: "Oldest" },
64
+ { value: "-updatedAt", label: "Recently updated" },
65
+ ],
66
+ columns: TICKET_COLUMNS,
109
67
  mapRows: (response) => toRecordArray(response.tickets).map((item, index) => ({
110
68
  id: toStringValue(item.id, `ticket-${index}`),
111
69
  primary: toStringValue(item.subject, "No subject"),
@@ -129,18 +87,29 @@ export function AdminSupportTicketsView({ children, ...props }) {
129
87
  return response.total;
130
88
  return mappedRows.length;
131
89
  },
132
- });
133
- const currentPage = table.getNumber("page", 1);
134
- const totalPages = Math.ceil(total / PAGE_SIZE);
135
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
136
- if (hasChildren) {
137
- return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
138
- }
139
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
140
- table.set("sort", v);
141
- }, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), _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 })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No support tickets found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
142
- { label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(row); setDrawerOpen(true); } },
143
- ] })), columns: buildTicketColumns(setSelectedRow, setDrawerOpen) })] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SUPPORT_TICKET_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Priority", tabs: ADMIN_SUPPORT_TICKET_PRIORITY_TABS, value: pendingFilters.priority ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, priority: id })) })] })] }), _jsx(AdminSupportTicketDetailView, { open: drawerOpen, onClose: () => setDrawerOpen(false), ticketId: selectedRow?.id, subject: selectedRow?.primary, userDisplayName: toStringValue(selectedRow?._raw?.userDisplayName, ""), category: toStringValue(selectedRow?._raw?.category, "general"), currentStatus: selectedRow?.status, currentPriority: toStringValue(selectedRow?._raw?.priority, "normal"), description: toStringValue(selectedRow?._raw?.description, ""), messages: Array.isArray(selectedRow?._raw?.messages)
90
+ buildFilters: (f) => {
91
+ const parts = [];
92
+ if (f.status && f.status !== "All")
93
+ parts.push(`status==${f.status}`);
94
+ if (f.priority && f.priority !== "All")
95
+ parts.push(`priority==${f.priority}`);
96
+ return parts.join(",") || undefined;
97
+ },
98
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
99
+ {
100
+ label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label,
101
+ onClick: () => {
102
+ setSelectedRow(row);
103
+ setDrawerOpen(true);
104
+ },
105
+ },
106
+ ] })),
107
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SUPPORT_TICKET_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Priority", tabs: ADMIN_SUPPORT_TICKET_PRIORITY_TABS, value: pendingFilters.priority ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, priority: id })) })] })),
108
+ };
109
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminSupportTicketDetailView, { open: drawerOpen, onClose: () => setDrawerOpen(false), ticketId: selectedRow?.id, subject: selectedRow?.primary, userDisplayName: toStringValue(selectedRow?._raw?.userDisplayName, ""), category: toStringValue(selectedRow?._raw?.category, "general"), currentStatus: selectedRow?.status, currentPriority: toStringValue(selectedRow?._raw?.priority, "normal"), description: toStringValue(selectedRow?._raw?.description, ""), messages: Array.isArray(selectedRow?._raw?.messages)
144
110
  ? selectedRow._raw.messages
145
- : [], internalNotes: toStringValue(selectedRow?._raw?.internalNotes, "") || undefined, orderId: toStringValue(selectedRow?._raw?.orderId, "") || undefined })] }));
111
+ : [], internalNotes: toStringValue(selectedRow?._raw?.internalNotes, "") || undefined, orderId: toStringValue(selectedRow?._raw?.orderId, "") || undefined, relatedParties: selectedRow?._raw?.relatedParties &&
112
+ typeof selectedRow._raw.relatedParties === "object"
113
+ ? selectedRow._raw.relatedParties
114
+ : undefined })] }));
146
115
  }
@@ -1,5 +1,5 @@
1
- import type { ListingViewShellProps } from "../../../ui";
2
- export interface AdminTeamViewProps extends ListingViewShellProps {
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export interface AdminTeamViewProps extends ListingLayoutProps {
3
3
  onBulkRemove?: (ids: string[]) => Promise<void>;
4
4
  }
5
5
  export declare function AdminTeamView({ children, onBulkRemove, ...props }: AdminTeamViewProps): import("react/jsx-runtime").JSX.Element;