@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
@@ -1,24 +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 { X } from "lucide-react";
5
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text } from "../../../ui";
3
+ import React, { useState } from "react";
4
+ import { FilterChipGroup, ListingLayout, RowActionMenu, Text } from "../../../ui";
8
5
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
6
  import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
10
7
  import { ADMIN_SUPPORT_TICKET_STATUS_TABS, ADMIN_SUPPORT_TICKET_PRIORITY_TABS, } from "../constants/filter-tabs";
11
- import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
12
- import { DataTable } from "./DataTable";
8
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
9
+ import { DataListingView } from "./DataListingView";
13
10
  import { AdminSupportTicketDetailView } from "./AdminSupportTicketDetailView";
14
- const PAGE_SIZE = 25;
15
- const FILTER_KEYS = ["status", "priority"];
16
- const DEFAULT_SORT = "-createdAt";
17
- const SORT_OPTIONS = [
18
- { value: "-createdAt", label: "Newest" },
19
- { value: "createdAt", label: "Oldest" },
20
- { value: "-updatedAt", label: "Recently updated" },
21
- ];
22
11
  const PRIORITY_BADGE = {
23
12
  urgent: "bg-error-surface text-error",
24
13
  high: "bg-warning-surface text-warning",
@@ -32,86 +21,49 @@ const STATUS_BADGE = {
32
21
  resolved: "bg-success-surface text-success",
33
22
  closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
34
23
  };
35
- function SupportTicketsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
36
- if (!filterOpen)
37
- return null;
38
- 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" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", 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("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})` : ""] }) })] })] }));
39
- }
40
- function buildTicketColumns(setSelectedRow, setDrawerOpen) {
41
- return [
42
- {
43
- key: "primary",
44
- header: "Subject",
45
- render: (row) => {
46
- const priority = toStringValue(row._raw?.priority, "normal");
47
- 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 })] }));
48
- },
49
- },
50
- {
51
- key: "status",
52
- header: "Status",
53
- className: "w-36",
54
- 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 })] }));
55
31
  },
56
- {
57
- key: "updatedAt",
58
- header: "Updated",
59
- className: "w-32",
60
- render: (row) => (_jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
61
- },
62
- ];
63
- }
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
+ ];
64
46
  export function AdminSupportTicketsView({ children, ...props }) {
65
- const hasChildren = React.Children.count(children) > 0;
66
- const [view, setView] = useState("table");
67
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
68
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
69
- const [filterOpen, setFilterOpen] = useState(false);
70
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
71
47
  const [drawerOpen, setDrawerOpen] = useState(false);
72
48
  const [selectedRow, setSelectedRow] = useState(null);
73
- const openFilters = useCallback(() => {
74
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
75
- setFilterOpen(true);
76
- }, [table]);
77
- const applyFilters = useCallback(() => {
78
- const updates = { page: "1" };
79
- for (const k of FILTER_KEYS)
80
- updates[k] = pendingFilters[k] ?? "";
81
- table.setMany(updates);
82
- setFilterOpen(false);
83
- }, [pendingFilters, table]);
84
- const clearFilters = useCallback(() => {
85
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
86
- }, []);
87
- const resetAll = useCallback(() => {
88
- const updates = { q: "", sort: "" };
89
- for (const k of FILTER_KEYS)
90
- updates[k] = "";
91
- table.setMany(updates);
92
- setSearchInput("");
93
- }, [table]);
94
- const commitSearch = useCallback(() => {
95
- table.set("q", searchInput.trim());
96
- }, [searchInput, table]);
97
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
98
- const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
99
- const filterParts = [];
100
- const statusRaw = table.get("status");
101
- if (statusRaw && statusRaw !== "All")
102
- filterParts.push(`status==${statusRaw}`);
103
- const priorityRaw = table.get("priority");
104
- if (priorityRaw && priorityRaw !== "All")
105
- filterParts.push(`priority==${priorityRaw}`);
106
- const filters = filterParts.join(",") || undefined;
107
- 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",
108
59
  queryKey: ["admin", "support-tickets", "listing"],
109
60
  endpoint: ADMIN_ENDPOINTS.SUPPORT_TICKETS,
110
- page: table.getNumber("page", 1),
111
- pageSize: PAGE_SIZE,
112
- sorts: table.get("sort") || DEFAULT_SORT,
113
- filters,
114
- 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,
115
67
  mapRows: (response) => toRecordArray(response.tickets).map((item, index) => ({
116
68
  id: toStringValue(item.id, `ticket-${index}`),
117
69
  primary: toStringValue(item.subject, "No subject"),
@@ -135,18 +87,29 @@ export function AdminSupportTicketsView({ children, ...props }) {
135
87
  return response.total;
136
88
  return mappedRows.length;
137
89
  },
138
- });
139
- const currentPage = table.getNumber("page", 1);
140
- const totalPages = Math.ceil(total / PAGE_SIZE);
141
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
142
- if (hasChildren) {
143
- return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
144
- }
145
- 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) => {
146
- table.set("sort", v);
147
- }, 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: [
148
- { label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(row); setDrawerOpen(true); } },
149
- ] })), columns: buildTicketColumns(setSelectedRow, setDrawerOpen) })] }), _jsx(SupportTicketsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _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)
150
110
  ? selectedRow._raw.messages
151
- : [], 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 })] }));
152
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;
@@ -1,25 +1,15 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
- import { X, UserPlus } from "lucide-react";
5
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { BulkActionBar, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, } from "../../../ui";
8
- import { useBottomActions } from "../../layout";
4
+ import { UserPlus } from "lucide-react";
5
+ import { Button, FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
9
6
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
7
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
8
+ import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
11
9
  import { ALL_TAB } from "../constants/filter-tabs";
12
- import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
13
- import { DataTable } from "./DataTable";
10
+ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
11
+ import { DataListingView } from "./DataListingView";
14
12
  import { AdminEmployeeEditorView } from "./AdminEmployeeEditorView";
15
- const PAGE_SIZE = 25;
16
- const FILTER_KEY = "group";
17
- const DEFAULT_SORT = "-createdAt";
18
- const SORT_OPTIONS = [
19
- { value: "-createdAt", label: "Newest" },
20
- { value: "createdAt", label: "Oldest" },
21
- { value: "displayName", label: "Name A–Z" },
22
- ];
23
13
  const GROUP_TABS = [
24
14
  ALL_TAB,
25
15
  { id: "content_moderator", label: "Content Mod" },
@@ -45,41 +35,10 @@ const GROUP_TABS = [
45
35
  function formatGroup(group) {
46
36
  return group.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
47
37
  }
48
- function TeamFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingGroup, setPendingGroup, applyFilters, }) {
49
- if (!filterOpen)
50
- return null;
51
- 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", children: _jsx(FilterChipGroup, { label: "Permission Group", tabs: GROUP_TABS, value: pendingGroup, onChange: (id) => setPendingGroup(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})` : ""] }) })] })] }));
52
- }
53
38
  export function AdminTeamView({ children, onBulkRemove, ...props }) {
54
- const hasChildren = React.Children.count(children) > 0;
55
- const [view, setView] = useState("table");
56
- const table = useUrlTable({
57
- defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT },
58
- });
59
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
60
- const [filterOpen, setFilterOpen] = useState(false);
61
- const [pendingGroup, setPendingGroup] = useState(table.get(FILTER_KEY) ?? "");
62
39
  const [drawerOpen, setDrawerOpen] = useState(false);
63
40
  const [selectedRow, setSelectedRow] = useState(null);
64
41
  const [inviteMode, setInviteMode] = useState(false);
65
- const openFilters = useCallback(() => {
66
- setPendingGroup(table.get(FILTER_KEY) ?? "");
67
- setFilterOpen(true);
68
- }, [table]);
69
- const applyFilters = useCallback(() => {
70
- table.setMany({ page: "1", [FILTER_KEY]: pendingGroup });
71
- setFilterOpen(false);
72
- }, [pendingGroup, table]);
73
- const clearFilters = useCallback(() => {
74
- setPendingGroup("");
75
- }, []);
76
- const resetAll = useCallback(() => {
77
- table.setMany({ q: "", sort: "", [FILTER_KEY]: "" });
78
- setSearchInput("");
79
- }, [table]);
80
- const commitSearch = useCallback(() => {
81
- table.set("q", searchInput.trim());
82
- }, [searchInput, table]);
83
42
  const openInvite = useCallback(() => {
84
43
  setSelectedRow(null);
85
44
  setInviteMode(true);
@@ -90,22 +49,23 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
90
49
  setInviteMode(false);
91
50
  setDrawerOpen(true);
92
51
  }, []);
93
- const activeFilterCount = table.get(FILTER_KEY) ? 1 : 0;
94
- const hasActiveState = !!table.get("q") ||
95
- table.get("sort") !== DEFAULT_SORT ||
96
- activeFilterCount > 0;
97
- const groupRaw = table.get(FILTER_KEY);
98
- const filters = groupRaw && groupRaw !== "All"
99
- ? `permissionGroup==${groupRaw}`
100
- : undefined;
101
- const { rows, total, isLoading, errorMessage } = useAdminListingData({
52
+ if (React.Children.count(children) > 0) {
53
+ return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
54
+ }
55
+ const config = {
56
+ portal: "admin",
57
+ title: "Team",
58
+ searchPlaceholder: "Search by name or email",
59
+ emptyLabel: "No employees found",
60
+ filterKeys: ["group"],
61
+ defaultSort: "-createdAt",
102
62
  queryKey: ["admin", "team", "listing"],
103
63
  endpoint: ADMIN_ENDPOINTS.TEAM,
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,
64
+ sortOptions: [
65
+ { value: "-createdAt", label: "Newest" },
66
+ { value: "createdAt", label: "Oldest" },
67
+ { value: "displayName", label: "Name A–Z" },
68
+ ],
109
69
  mapRows: (response) => toRecordArray(response.users).map((item, index) => ({
110
70
  id: toStringValue(item.id ?? item.uid, `employee-${index}`),
111
71
  primary: toStringValue(item.displayName, "Unnamed employee"),
@@ -116,7 +76,9 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
116
76
  : "Custom",
117
77
  ].join(" · "),
118
78
  status: typeof item.disabled === "boolean"
119
- ? item.disabled ? "Disabled" : "Active"
79
+ ? item.disabled
80
+ ? "Disabled"
81
+ : "Active"
120
82
  : "Active",
121
83
  updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
122
84
  _raw: item,
@@ -128,30 +90,25 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
128
90
  return response.total;
129
91
  return mappedRows.length;
130
92
  },
131
- });
132
- const currentPage = table.getNumber("page", 1);
133
- const totalPages = Math.ceil(total / PAGE_SIZE);
134
- const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
135
- const bulkActions = [
136
- ...(onBulkRemove ? [{
137
- id: "bulk-remove",
138
- label: ACTIONS.ADMIN["remove-team-member"].label,
139
- variant: "danger",
140
- onClick: async () => { await onBulkRemove(selection.selectedIds); selection.clearSelection(); },
141
- }] : []),
142
- ];
143
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
144
- if (hasChildren) {
145
- return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
146
- }
147
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by name or email", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
148
- table.set("sort", v);
149
- }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] }) }), 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 employees found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
150
- {
151
- label: ACTIONS.ADMIN["edit-team-member"].label,
152
- onClick: () => openEdit(row),
153
- },
154
- ] })) })] }), _jsx(TeamFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingGroup: pendingGroup, setPendingGroup: setPendingGroup, applyFilters: applyFilters })] }), _jsx(AdminEmployeeEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), mode: inviteMode ? "invite" : "edit", userId: selectedRow?.id, displayName: selectedRow?.primary, currentPermissionGroup: toStringValue(selectedRow?._raw?.permissionGroup, "custom"), currentPermissions: Array.isArray(selectedRow?._raw?.permissions)
93
+ buildFilters: (state) => state.group && state.group !== "All" ? `permissionGroup==${state.group}` : undefined,
94
+ toolbarExtra: (_jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] })),
95
+ buildBulkActions: onBulkRemove
96
+ ? (selection) => [
97
+ buildBulkAction(ACTIONS.ADMIN["remove-team-member"], async () => {
98
+ await onBulkRemove(selection.selectedIds);
99
+ selection.clearSelection();
100
+ }),
101
+ ]
102
+ : undefined,
103
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
104
+ {
105
+ label: ACTIONS.ADMIN["edit-team-member"].label,
106
+ onClick: () => openEdit(row),
107
+ },
108
+ ] })),
109
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Permission Group", tabs: GROUP_TABS, value: pendingFilters.group ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, group: id })) })),
110
+ };
111
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminEmployeeEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), mode: inviteMode ? "invite" : "edit", userId: selectedRow?.id, displayName: selectedRow?.primary, currentPermissionGroup: toStringValue(selectedRow?._raw?.permissionGroup, "custom"), currentPermissions: Array.isArray(selectedRow?._raw?.permissions)
155
112
  ? selectedRow._raw.permissions
156
113
  : [] })] }));
157
114
  }
@@ -21,6 +21,16 @@ export interface AdminUserEditorViewProps {
21
21
  /** Whether the user is hard-banned (isDisabled + hardBanReason set). */
22
22
  currentIsHardBanned?: boolean;
23
23
  currentHardBanReason?: string;
24
+ currentPhoneNumber?: string | null;
25
+ currentBio?: string;
26
+ currentLocation?: string;
27
+ currentWebsite?: string;
28
+ currentSocialLinks?: {
29
+ twitter?: string;
30
+ instagram?: string;
31
+ facebook?: string;
32
+ linkedin?: string;
33
+ };
24
34
  }
25
- export declare function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled, currentEmailVerified, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, }: AdminUserEditorViewProps): import("react/jsx-runtime").JSX.Element;
35
+ export declare function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled, currentEmailVerified, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }: AdminUserEditorViewProps): import("react/jsx-runtime").JSX.Element;
26
36
  export {};
@@ -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, useQueryClient } from "@tanstack/react-query";
5
- import { Button, ConfirmDeleteModal, Form, FormActions, Heading, Select, SideDrawer, Text, Toggle, useToast } from "../../../ui";
5
+ import { Button, ConfirmDeleteModal, Form, FormActions, Heading, Input, Label, Select, SideDrawer, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
6
6
  import { apiClient } from "../../../http";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  const ROLE_OPTIONS = [
@@ -20,6 +20,10 @@ const BANNED_ACTION_OPTIONS = [
20
20
  { label: "Create support tickets", value: "create_support_tickets" },
21
21
  { label: "Report scammers", value: "report_scammers" },
22
22
  ];
23
+ // Standard textarea/input chrome for raw <textarea>/<input> elements in this
24
+ // view (consistent border/focus styling). Deduped to satisfy
25
+ // audit-code-quality REPEATED_STRING rule.
26
+ const TEXTAREA_CHROME = "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";
23
27
  function formatBanAction(action) {
24
28
  return BANNED_ACTION_OPTIONS.find((o) => o.value === action)?.label ?? action;
25
29
  }
@@ -37,10 +41,10 @@ function HardBanPanel({ userId, isHardBanned, currentHardBanReason, showHardBanF
37
41
  return (_jsxs("div", { className: "mb-4 rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Hard ban" }), isHardBanned ? (_jsx("span", { className: "rounded-full bg-error-surface px-2 py-0.5 text-xs font-semibold text-error", children: "Banned" })) : (_jsx("span", { className: "rounded-full bg-success-surface px-2 py-0.5 text-xs font-semibold text-success", children: "Active" }))] }), isHardBanned ? (_jsxs("div", { className: "space-y-2", children: [currentHardBanReason && _jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-400", children: ["Reason: ", currentHardBanReason] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: unbanPending, disabled: unbanPending, onClick: onUnban, children: "Lift hard ban" })] })) : showHardBanForm ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Ban reason (required)" }), _jsx("textarea", { value: hardBanReasonInput, onChange: (e) => setHardBanReasonInput(e.target.value), rows: 2, placeholder: "e.g. Repeated fraud, scam activity\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-red-500" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "danger", size: "sm", isLoading: hardBanPending, disabled: !hardBanReasonInput.trim() || hardBanPending, onClick: () => onHardBan(hardBanReasonInput.trim()), children: "Confirm hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowHardBanForm(false); setHardBanReasonInput(""); }, children: "Cancel" })] })] })) : (_jsx(Button, { type: "button", variant: "danger", size: "sm", disabled: !userId, onClick: () => setShowHardBanForm(true), children: "Impose hard ban" }))] }));
38
42
  }
39
43
  function SoftBanPanel({ userId, softBans, showAddSoftBan, setShowAddSoftBan, softBanAction, setSoftBanAction, softBanReason, setSoftBanReason, softBanExpiry, setSoftBanExpiry, softBanPending, liftPending, onAddSoftBan, onLiftSoftBan, }) {
40
- return (_jsxs("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: ["Soft bans", softBans.length > 0 ? ` (${softBans.length})` : ""] }), !showAddSoftBan && (_jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: !userId, onClick: () => setShowAddSoftBan(true), children: "Add soft ban" }))] }), softBans.length > 0 && (_jsx("ul", { className: "mb-3 space-y-2", children: softBans.map((ban) => (_jsxs("li", { className: "flex items-start justify-between gap-2 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-800", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold text-zinc-800 dark:text-zinc-200", children: formatBanAction(ban.action) }), _jsx("div", { className: "text-zinc-500 dark:text-zinc-400", children: ban.reason }), _jsx("div", { className: "text-zinc-400 dark:text-zinc-400", children: formatExpiry(ban.expiresAt) })] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftPending, disabled: liftPending, onClick: () => onLiftSoftBan(ban.action), children: "Lift" })] }, ban.action))) })), showAddSoftBan && (_jsxs("div", { className: "space-y-2", children: [_jsx(Select, { label: "Action to restrict", options: BANNED_ACTION_OPTIONS, value: softBanAction, onValueChange: setSoftBanAction }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Reason (required)" }), _jsx("textarea", { value: softBanReason, onChange: (e) => setSoftBanReason(e.target.value), rows: 2, placeholder: "e.g. Suspicious bid activity\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", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Expires at (optional \u2014 leave blank for permanent)" }), _jsx("input", { type: "datetime-local", value: softBanExpiry, onChange: (e) => setSoftBanExpiry(e.target.value), 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 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: softBanPending, disabled: !softBanReason.trim() || softBanPending, onClick: () => onAddSoftBan({ action: softBanAction, reason: softBanReason.trim(), ...(softBanExpiry ? { expiresAt: new Date(softBanExpiry).toISOString() } : {}) }), children: "Apply soft ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowAddSoftBan(false); setSoftBanReason(""); setSoftBanExpiry(""); }, children: "Cancel" })] })] })), softBans.length === 0 && !showAddSoftBan && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: "No active soft bans." }))] }));
44
+ return (_jsxs("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: ["Soft bans", softBans.length > 0 ? ` (${softBans.length})` : ""] }), !showAddSoftBan && (_jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: !userId, onClick: () => setShowAddSoftBan(true), children: "Add soft ban" }))] }), softBans.length > 0 && (_jsx("ul", { className: "mb-3 space-y-2", children: softBans.map((ban) => (_jsxs("li", { className: "flex items-start justify-between gap-2 rounded-md border border-zinc-200 bg-white px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-800", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-semibold text-zinc-800 dark:text-zinc-200", children: formatBanAction(ban.action) }), _jsx("div", { className: "text-zinc-500 dark:text-zinc-400", children: ban.reason }), _jsx("div", { className: "text-zinc-400 dark:text-zinc-400", children: formatExpiry(ban.expiresAt) })] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftPending, disabled: liftPending, onClick: () => onLiftSoftBan(ban.action), children: "Lift" })] }, ban.action))) })), showAddSoftBan && (_jsxs("div", { className: "space-y-2", children: [_jsx(Select, { label: "Action to restrict", options: BANNED_ACTION_OPTIONS, value: softBanAction, onValueChange: setSoftBanAction }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Reason (required)" }), _jsx("textarea", { value: softBanReason, onChange: (e) => setSoftBanReason(e.target.value), rows: 2, placeholder: "e.g. Suspicious bid activity\u2026", className: TEXTAREA_CHROME })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: "Expires at (optional \u2014 leave blank for permanent)" }), _jsx("input", { type: "datetime-local", value: softBanExpiry, onChange: (e) => setSoftBanExpiry(e.target.value), 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 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: softBanPending, disabled: !softBanReason.trim() || softBanPending, onClick: () => onAddSoftBan({ action: softBanAction, reason: softBanReason.trim(), ...(softBanExpiry ? { expiresAt: new Date(softBanExpiry).toISOString() } : {}) }), children: "Apply soft ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowAddSoftBan(false); setSoftBanReason(""); setSoftBanExpiry(""); }, children: "Cancel" })] })] })), softBans.length === 0 && !showAddSoftBan && (_jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: "No active soft bans." }))] }));
41
45
  }
42
46
  // --- Component ---------------------------------------------------------------
43
- export function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled, currentEmailVerified, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, }) {
47
+ export function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled, currentEmailVerified, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }) {
44
48
  const queryClient = useQueryClient();
45
49
  const { showToast } = useToast();
46
50
  // --- General fields -------------------------------------------------------
@@ -48,6 +52,16 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
48
52
  const [emailVerified, setEmailVerified] = React.useState(currentEmailVerified ?? false);
49
53
  const [adminNotes, setAdminNotes] = React.useState("");
50
54
  const [deleteOpen, setDeleteOpen] = React.useState(false);
55
+ // --- ST-2 profile fields --------------------------------------------------
56
+ const [editDisplayName, setEditDisplayName] = React.useState(displayName ?? "");
57
+ const [phoneNumber, setPhoneNumber] = React.useState(currentPhoneNumber ?? "");
58
+ const [bio, setBio] = React.useState(currentBio ?? "");
59
+ const [location, setLocation] = React.useState(currentLocation ?? "");
60
+ const [website, setWebsite] = React.useState(currentWebsite ?? "");
61
+ const [twitter, setTwitter] = React.useState(currentSocialLinks?.twitter ?? "");
62
+ const [instagram, setInstagram] = React.useState(currentSocialLinks?.instagram ?? "");
63
+ const [facebook, setFacebook] = React.useState(currentSocialLinks?.facebook ?? "");
64
+ const [linkedin, setLinkedin] = React.useState(currentSocialLinks?.linkedin ?? "");
51
65
  // --- Hard ban form --------------------------------------------------------
52
66
  const [showHardBanForm, setShowHardBanForm] = React.useState(false);
53
67
  const [hardBanReasonInput, setHardBanReasonInput] = React.useState("");
@@ -61,6 +75,15 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
61
75
  setRole(currentRole ?? "user");
62
76
  setEmailVerified(currentEmailVerified ?? false);
63
77
  setAdminNotes("");
78
+ setEditDisplayName(displayName ?? "");
79
+ setPhoneNumber(currentPhoneNumber ?? "");
80
+ setBio(currentBio ?? "");
81
+ setLocation(currentLocation ?? "");
82
+ setWebsite(currentWebsite ?? "");
83
+ setTwitter(currentSocialLinks?.twitter ?? "");
84
+ setInstagram(currentSocialLinks?.instagram ?? "");
85
+ setFacebook(currentSocialLinks?.facebook ?? "");
86
+ setLinkedin(currentSocialLinks?.linkedin ?? "");
64
87
  setShowHardBanForm(false);
65
88
  setHardBanReasonInput("");
66
89
  setShowAddSoftBan(false);
@@ -68,15 +91,51 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
68
91
  setSoftBanReason("");
69
92
  setSoftBanExpiry("");
70
93
  }
71
- }, [open, currentRole, currentEmailVerified]);
94
+ }, [
95
+ open,
96
+ currentRole,
97
+ currentEmailVerified,
98
+ displayName,
99
+ currentPhoneNumber,
100
+ currentBio,
101
+ currentLocation,
102
+ currentWebsite,
103
+ currentSocialLinks?.twitter,
104
+ currentSocialLinks?.instagram,
105
+ currentSocialLinks?.facebook,
106
+ currentSocialLinks?.linkedin,
107
+ ]);
72
108
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ["admin", "users"] });
73
109
  // --- Mutations ------------------------------------------------------------
74
110
  const saveMutation = useMutation({
75
111
  mutationFn: async () => {
112
+ // ST-2 — build publicProfile partial only when something changed; keeps
113
+ // the PATCH payload minimal and avoids overwriting unrelated subkeys.
114
+ const socialLinks = {};
115
+ if (twitter.trim())
116
+ socialLinks.twitter = twitter.trim();
117
+ if (instagram.trim())
118
+ socialLinks.instagram = instagram.trim();
119
+ if (facebook.trim())
120
+ socialLinks.facebook = facebook.trim();
121
+ if (linkedin.trim())
122
+ socialLinks.linkedin = linkedin.trim();
123
+ const publicProfile = {};
124
+ if (bio.trim())
125
+ publicProfile.bio = bio.trim();
126
+ if (location.trim())
127
+ publicProfile.location = location.trim();
128
+ if (website.trim())
129
+ publicProfile.website = website.trim();
130
+ if (Object.keys(socialLinks).length > 0)
131
+ publicProfile.socialLinks = socialLinks;
76
132
  await apiClient.patch(ADMIN_ENDPOINTS.USER_BY_ID(userId), {
77
133
  role,
78
134
  emailVerified,
79
135
  adminNotes: adminNotes || undefined,
136
+ displayName: editDisplayName.trim() || undefined,
137
+ phoneNumber: phoneNumber.trim() || undefined,
138
+ publicProfile: Object.keys(publicProfile).length > 0 ? publicProfile : undefined,
80
139
  });
81
140
  },
82
141
  onSuccess: () => {
@@ -160,8 +219,23 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
160
219
  });
161
220
  const isHardBanned = currentIsHardBanned ?? false;
162
221
  const softBans = currentSoftBans ?? [];
163
- return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: displayName ? `Manage: ${displayName}` : "Manage User", children: _jsxs(Form, { onSubmit: (e) => {
222
+ const renderInfoCard = () => userId ? (_jsx("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-900/40", children: _jsxs("div", { className: "flex flex-col gap-1 text-zinc-700 dark:text-zinc-300", children: [_jsxs(_Fragment, { children: [_jsx("span", { className: "font-semibold", children: "Owner ID (Firebase UID):" }), " ", _jsx("code", { className: "select-all font-mono", children: userId })] }), ownedStoreId && (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-semibold", children: "Owns store:" }), " ", _jsx("code", { className: "select-all font-mono", children: ownedStoreId }), ownedStoreName ? ` — ${ownedStoreName}` : ""] }))] }) })) : null;
223
+ const renderRoleSection = () => (_jsx(Select, { label: "Role", options: ROLE_OPTIONS, value: role, onValueChange: setRole }));
224
+ const renderEmailVerifiedSection = () => (_jsx(Toggle, { label: "Email verified", checked: emailVerified, onChange: setEmailVerified }));
225
+ const renderProfileSection = () => (_jsxs("div", { className: "border-t border-zinc-200 pt-3 dark:border-zinc-700", children: [_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Profile details" }), _jsxs("div", { className: "space-y-3", children: [_jsx(Input, { label: "Display name", value: editDisplayName, onChange: (e) => setEditDisplayName(e.target.value), placeholder: "Full name shown on profile" }), _jsx(Input, { label: "Phone number", type: "tel", value: phoneNumber, onChange: (e) => setPhoneNumber(e.target.value), placeholder: "+91 90000 00000" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { children: "Bio" }), _jsx("textarea", { value: bio, onChange: (e) => setBio(e.target.value), rows: 3, placeholder: "Short bio shown on the public profile\u2026", className: TEXTAREA_CHROME })] }), _jsx(Input, { label: "Location", value: location, onChange: (e) => setLocation(e.target.value), placeholder: "City, Country" }), _jsx(Input, { label: "Website", type: "url", value: website, onChange: (e) => setWebsite(e.target.value), placeholder: "https://" }), _jsx(Text, { size: "xs", color: "muted", children: "Social links" }), _jsx(Input, { label: "Twitter / X", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "@handle or full URL" }), _jsx(Input, { label: "Instagram", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "@handle or full URL" }), _jsx(Input, { label: "Facebook", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/\u2026" }), _jsx(Input, { label: "LinkedIn", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/in/\u2026" })] })] }));
226
+ const renderAdminNotesSection = () => (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { children: "Admin notes (optional)" }), _jsx("textarea", { value: adminNotes, onChange: (e) => setAdminNotes(e.target.value), rows: 3, placeholder: "Internal notes about this user\u2026", className: TEXTAREA_CHROME })] }));
227
+ const renderActionsSection = () => (_jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "danger", onClick: () => setDeleteOpen(true), disabled: !userId, children: "Delete user" }), _jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !userId || saveMutation.isPending, children: "Save changes" })] }));
228
+ const renderModerationSection = () => (_jsxs("div", { className: "border-t border-zinc-200 pt-4 dark:border-zinc-700", children: [_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Moderation" }), _jsx(HardBanPanel, { userId: userId, isHardBanned: isHardBanned, currentHardBanReason: currentHardBanReason, showHardBanForm: showHardBanForm, setShowHardBanForm: setShowHardBanForm, hardBanReasonInput: hardBanReasonInput, setHardBanReasonInput: setHardBanReasonInput, hardBanPending: hardBanMutation.isPending, unbanPending: unbanMutation.isPending, onHardBan: (reason) => hardBanMutation.mutate(reason), onUnban: () => unbanMutation.mutate() }), _jsx(SoftBanPanel, { userId: userId, softBans: softBans, showAddSoftBan: showAddSoftBan, setShowAddSoftBan: setShowAddSoftBan, softBanAction: softBanAction, setSoftBanAction: setSoftBanAction, softBanReason: softBanReason, setSoftBanReason: setSoftBanReason, softBanExpiry: softBanExpiry, setSoftBanExpiry: setSoftBanExpiry, softBanPending: softBanMutation.isPending, liftPending: liftSoftBanMutation.isPending, onAddSoftBan: (payload) => softBanMutation.mutate(payload), onLiftSoftBan: (action) => liftSoftBanMutation.mutate(action) })] }));
229
+ return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: displayName ? `Manage: ${displayName}` : "Manage User", children: _jsx(Form, { onSubmit: (e) => {
164
230
  e.preventDefault();
165
231
  saveMutation.mutate();
166
- }, className: "space-y-4 p-4", children: [userId && (_jsx("div", { className: "rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-xs dark:border-zinc-700 dark:bg-zinc-900/40", children: _jsxs("div", { className: "flex flex-col gap-1 text-zinc-700 dark:text-zinc-300", children: [_jsxs(_Fragment, { children: [_jsx("span", { className: "font-semibold", children: "Owner ID (Firebase UID):" }), " ", _jsx("code", { className: "select-all font-mono", children: userId })] }), ownedStoreId && (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-semibold", children: "Owns store:" }), " ", _jsx("code", { className: "select-all font-mono", children: ownedStoreId }), ownedStoreName ? ` — ${ownedStoreName}` : ""] }))] }) })), _jsx(Select, { label: "Role", options: ROLE_OPTIONS, value: role, onValueChange: setRole }), _jsx(Toggle, { label: "Email verified", checked: emailVerified, onChange: setEmailVerified }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Admin notes (optional)" }), _jsx("textarea", { value: adminNotes, onChange: (e) => setAdminNotes(e.target.value), rows: 3, placeholder: "Internal notes about this user\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: "danger", onClick: () => setDeleteOpen(true), disabled: !userId, children: "Delete user" }), _jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !userId || saveMutation.isPending, children: "Save changes" })] }), _jsxs("div", { className: "border-t border-zinc-200 pt-4 dark:border-zinc-700", children: [_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Moderation" }), _jsx(HardBanPanel, { userId: userId, isHardBanned: isHardBanned, currentHardBanReason: currentHardBanReason, showHardBanForm: showHardBanForm, setShowHardBanForm: setShowHardBanForm, hardBanReasonInput: hardBanReasonInput, setHardBanReasonInput: setHardBanReasonInput, hardBanPending: hardBanMutation.isPending, unbanPending: unbanMutation.isPending, onHardBan: (reason) => hardBanMutation.mutate(reason), onUnban: () => unbanMutation.mutate() }), _jsx(SoftBanPanel, { userId: userId, softBans: softBans, showAddSoftBan: showAddSoftBan, setShowAddSoftBan: setShowAddSoftBan, softBanAction: softBanAction, setSoftBanAction: setSoftBanAction, softBanReason: softBanReason, setSoftBanReason: setSoftBanReason, softBanExpiry: softBanExpiry, setSoftBanExpiry: setSoftBanExpiry, softBanPending: softBanMutation.isPending, liftPending: liftSoftBanMutation.isPending, onAddSoftBan: (payload) => softBanMutation.mutate(payload), onLiftSoftBan: (action) => liftSoftBanMutation.mutate(action) })] })] }) }), _jsx(ConfirmDeleteModal, { isOpen: deleteOpen, onClose: () => setDeleteOpen(false), onConfirm: () => deleteMutation.mutate(), isDeleting: deleteMutation.isPending, title: `Delete ${displayName ?? "user"}?`, message: "This action cannot be undone. The user's account and all associated data will be permanently removed.", confirmText: "Delete user", variant: "danger" })] }));
232
+ }, className: "p-4", children: _jsx(StackedViewShell, { portal: "admin", className: "space-y-4", sections: [
233
+ renderInfoCard,
234
+ renderRoleSection,
235
+ renderEmailVerifiedSection,
236
+ renderProfileSection,
237
+ renderAdminNotesSection,
238
+ renderActionsSection,
239
+ renderModerationSection,
240
+ ] }) }) }), _jsx(ConfirmDeleteModal, { isOpen: deleteOpen, onClose: () => setDeleteOpen(false), onConfirm: () => deleteMutation.mutate(), isDeleting: deleteMutation.isPending, title: `Delete ${displayName ?? "user"}?`, message: "This action cannot be undone. The user's account and all associated data will be permanently removed.", confirmText: "Delete user", variant: "danger" })] }));
167
241
  }
@@ -1,4 +1,3 @@
1
- import type { ListingViewShellProps } from "../../../ui";
2
- export interface AdminUsersViewProps extends ListingViewShellProps {
3
- }
1
+ import type { ListingLayoutProps } from "../../../ui";
2
+ export type AdminUsersViewProps = ListingLayoutProps;
4
3
  export declare function AdminUsersView({ children, ...props }: AdminUsersViewProps): import("react/jsx-runtime").JSX.Element;