@mohasinac/appkit 4.10.0 → 4.11.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 (319) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
  3. package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
  4. package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
  5. package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
  6. package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
  7. package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
  8. package/dist/_internal/server/features/audit-log/actions.js +38 -0
  9. package/dist/_internal/server/features/brands/actions.js +0 -2
  10. package/dist/_internal/server/features/brands/og.d.ts +4 -1
  11. package/dist/_internal/server/features/brands/og.js +1 -1
  12. package/dist/_internal/server/features/grouped/data.d.ts +4 -0
  13. package/dist/_internal/server/features/grouped/data.js +24 -0
  14. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  15. package/dist/_internal/server/features/grouped/index.js +1 -1
  16. package/dist/_internal/server/features/orders/actions.js +9 -0
  17. package/dist/_internal/server/features/orders/adapters.js +25 -0
  18. package/dist/_internal/server/functions/scheduled.d.ts +3 -1
  19. package/dist/_internal/server/functions/scheduled.js +17 -1
  20. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
  21. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
  22. package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
  23. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
  24. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
  25. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
  26. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
  27. package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
  28. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
  29. package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
  30. package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
  31. package/dist/_internal/server/jobs/handlers/index.js +3 -0
  32. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
  33. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
  34. package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
  35. package/dist/_internal/server/notifications/channel-health.js +101 -0
  36. package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
  37. package/dist/_internal/shared/features/brands/schema.js +0 -1
  38. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
  39. package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
  40. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  41. package/dist/client.d.ts +21 -6
  42. package/dist/client.js +13 -3
  43. package/dist/constants/api-endpoints.d.ts +6 -9
  44. package/dist/constants/api-endpoints.js +2 -3
  45. package/dist/contracts/client-auth.d.ts +3 -7
  46. package/dist/contracts/email.d.ts +1 -0
  47. package/dist/features/account/components/AddressForm.js +24 -2
  48. package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
  49. package/dist/features/account/components/BecomeSellerView.js +1 -1
  50. package/dist/features/account/components/NotificationBell.js +1 -1
  51. package/dist/features/account/components/UserBidsView.d.ts +1 -0
  52. package/dist/features/account/components/UserBidsView.js +47 -0
  53. package/dist/features/account/components/UserOrdersView.d.ts +4 -11
  54. package/dist/features/account/components/UserOrdersView.js +54 -4
  55. package/dist/features/account/components/UserReturnsView.d.ts +4 -9
  56. package/dist/features/account/components/UserReturnsView.js +37 -3
  57. package/dist/features/account/components/index.d.ts +3 -2
  58. package/dist/features/account/components/index.js +1 -0
  59. package/dist/features/admin/actions/admin-actions.js +22 -0
  60. package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
  61. package/dist/features/admin/actions/notification-actions.js +4 -3
  62. package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
  63. package/dist/features/admin/components/AdminAdsView.js +86 -89
  64. package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
  65. package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
  66. package/dist/features/admin/components/AdminAuditLogView.js +95 -0
  67. package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
  68. package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
  69. package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
  70. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
  71. package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
  72. package/dist/features/admin/components/AdminCartsView.js +34 -4
  73. package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
  74. package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
  75. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  76. package/dist/features/admin/components/AdminHistoryView.js +11 -1
  77. package/dist/features/admin/components/AdminNotificationsView.js +27 -1
  78. package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
  79. package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
  80. package/dist/features/admin/components/AdminOrdersView.js +92 -24
  81. package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
  82. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
  83. package/dist/features/admin/components/AdminPayoutsView.js +15 -31
  84. package/dist/features/admin/components/AdminProductEditorView.js +26 -3
  85. package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
  86. package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
  87. package/dist/features/admin/components/AdminStoresView.js +7 -0
  88. package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
  89. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
  90. package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
  91. package/dist/features/admin/components/AdminUserEditorView.js +4 -2
  92. package/dist/features/admin/components/AdminUsersView.js +1 -1
  93. package/dist/features/admin/components/AdminWishlistsView.js +14 -1
  94. package/dist/features/admin/components/DataListingView.d.ts +2 -2
  95. package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
  96. package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
  97. package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
  98. package/dist/features/admin/components/ViewNotificationModal.js +15 -0
  99. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
  100. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
  101. package/dist/features/admin/components/index.d.ts +8 -1
  102. package/dist/features/admin/components/index.js +4 -0
  103. package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
  104. package/dist/features/admin/constants/filter-tabs.js +27 -0
  105. package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
  106. package/dist/features/admin/hooks/useAdminListing.js +3 -1
  107. package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
  108. package/dist/features/admin/hooks/useAdminListingData.js +9 -2
  109. package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
  110. package/dist/features/admin/schemas/firestore.d.ts +12 -0
  111. package/dist/features/analytics/types.d.ts +1 -1
  112. package/dist/features/analytics/types.js +8 -0
  113. package/dist/features/auctions/schemas/index.d.ts +5 -0
  114. package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
  115. package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
  116. package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
  117. package/dist/features/audit-log/schemas/firestore.js +35 -0
  118. package/dist/features/auth/actions/index.d.ts +0 -1
  119. package/dist/features/auth/actions/index.js +0 -1
  120. package/dist/features/auth/hooks/useAuth.d.ts +8 -35
  121. package/dist/features/auth/hooks/useAuth.js +21 -37
  122. package/dist/features/auth/permissions/constants.d.ts +1 -1
  123. package/dist/features/auth/permissions/constants.js +2 -0
  124. package/dist/features/blog/schemas/index.d.ts +32 -0
  125. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
  126. package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
  127. package/dist/features/categories/components/BrandDetailPageView.js +14 -12
  128. package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
  129. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
  130. package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
  131. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
  132. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  133. package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
  134. package/dist/features/categories/schemas/firestore.d.ts +19 -2
  135. package/dist/features/categories/schemas/index.d.ts +24 -3
  136. package/dist/features/categories/schemas/index.js +3 -1
  137. package/dist/features/categories/types/index.d.ts +7 -0
  138. package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
  139. package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
  140. package/dist/features/contact/components/ContactForm.js +17 -4
  141. package/dist/features/contact/email.d.ts +1 -0
  142. package/dist/features/contact/email.js +3 -0
  143. package/dist/features/events/components/AdminEventEditorView.js +54 -2
  144. package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
  145. package/dist/features/events/components/AdminEventEntriesView.js +56 -89
  146. package/dist/features/events/components/EventFormDrawer.js +5 -1
  147. package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
  148. package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
  149. package/dist/features/events/schemas/index.d.ts +22 -0
  150. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +4 -0
  151. package/dist/features/grouped/repository/grouped-listings.repository.js +18 -0
  152. package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
  153. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
  154. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  155. package/dist/features/layout/TitleBarLayout.d.ts +1 -1
  156. package/dist/features/layout/TitleBarLayout.js +3 -3
  157. package/dist/features/media/MediaImage.js +39 -3
  158. package/dist/features/media/types/index.d.ts +10 -0
  159. package/dist/features/media/types/index.js +1 -0
  160. package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
  161. package/dist/features/media/upload/MediaUploadField.js +11 -7
  162. package/dist/features/media/upload/video-poster.d.ts +2 -0
  163. package/dist/features/media/upload/video-poster.js +16 -1
  164. package/dist/features/media/upload/video-poster.test.js +1 -1
  165. package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
  166. package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
  167. package/dist/features/orders/components/index.d.ts +2 -0
  168. package/dist/features/orders/components/index.js +1 -0
  169. package/dist/features/orders/constants/payment-window.d.ts +25 -0
  170. package/dist/features/orders/constants/payment-window.js +25 -0
  171. package/dist/features/orders/repository/orders.repository.d.ts +32 -0
  172. package/dist/features/orders/repository/orders.repository.js +56 -0
  173. package/dist/features/orders/schemas/index.d.ts +24 -24
  174. package/dist/features/orders/types/index.d.ts +33 -0
  175. package/dist/features/pre-orders/schemas/index.d.ts +5 -0
  176. package/dist/features/products/components/BidHistory.js +9 -1
  177. package/dist/features/products/components/GroupSettingsPanel.js +25 -3
  178. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  179. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
  180. package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
  181. package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
  182. package/dist/features/products/components/ProductDetailActions.js +1 -1
  183. package/dist/features/products/components/ProductGrid.js +15 -1
  184. package/dist/features/products/constants/action-defs.d.ts +1 -0
  185. package/dist/features/products/constants/action-defs.js +1 -0
  186. package/dist/features/products/schemas/index.d.ts +5 -0
  187. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
  188. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  189. package/dist/features/search/components/SearchView.d.ts +1 -3
  190. package/dist/features/search/components/SearchView.js +1 -1
  191. package/dist/features/seller/components/QuickProductForm.js +2 -1
  192. package/dist/features/seller/components/SellerBidsView.js +66 -107
  193. package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
  194. package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
  195. package/dist/features/seller/components/SellerOrdersView.js +134 -163
  196. package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
  197. package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
  198. package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
  199. package/dist/features/seller/components/SellerPayoutsView.js +25 -5
  200. package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
  201. package/dist/features/seller/components/SellerProductShell.js +89 -36
  202. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
  203. package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
  204. package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
  205. package/dist/features/seller/components/SellerProductsView.js +131 -189
  206. package/dist/features/seller/components/SellerReviewsView.js +95 -133
  207. package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
  208. package/dist/features/seller/components/SellerShippingView.js +41 -4
  209. package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
  210. package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
  211. package/dist/features/seller/components/SellerStorefrontView.js +42 -5
  212. package/dist/features/seller/components/index.d.ts +2 -2
  213. package/dist/features/seller/components/index.js +2 -2
  214. package/dist/features/shell/FormShell.d.ts +24 -4
  215. package/dist/features/shell/FormShell.js +12 -6
  216. package/dist/features/shell/QuickFormDrawer.js +67 -12
  217. package/dist/features/shell/StepForm.d.ts +30 -1
  218. package/dist/features/shell/StepForm.js +47 -4
  219. package/dist/features/shell/index.d.ts +1 -1
  220. package/dist/features/shell/index.js +1 -1
  221. package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
  222. package/dist/features/shipments/schemas/validation.d.ts +14 -4
  223. package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
  224. package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
  225. package/dist/features/tester/components/TesterHubView.js +3 -2
  226. package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
  227. package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
  228. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +404 -15
  229. package/dist/index.d.ts +15 -3
  230. package/dist/index.js +22 -5
  231. package/dist/next/routing/route-map.d.ts +11 -0
  232. package/dist/next/routing/route-map.js +5 -0
  233. package/dist/providers/email-resend/provider.js +1 -0
  234. package/dist/providers/firebase-client/auth.d.ts +1 -0
  235. package/dist/providers/firebase-client/auth.js +7 -1
  236. package/dist/repositories/index.d.ts +1 -0
  237. package/dist/repositories/index.js +1 -0
  238. package/dist/schemas/registry.d.ts +61 -25
  239. package/dist/security/pii-mask.js +1 -1
  240. package/dist/seed/categories-seed-data.js +69 -0
  241. package/dist/seed/products-auctions-seed-data.js +11 -0
  242. package/dist/seed/products-standard-seed-data.js +93 -4
  243. package/dist/seed/site-settings-seed-data.js +13 -1
  244. package/dist/seed/store-extensions-seed-data.js +1 -1
  245. package/dist/server-entry.d.ts +2 -1
  246. package/dist/server-entry.js +20 -2
  247. package/dist/server.d.ts +7 -2
  248. package/dist/server.js +8 -3
  249. package/dist/styles.css +1 -1
  250. package/dist/tailwind-utilities.css +1 -1
  251. package/dist/ui/components/Form.d.ts +11 -2
  252. package/dist/ui/components/Form.js +4 -3
  253. package/dist/ui/components/FormField.d.ts +3 -1
  254. package/dist/ui/components/FormField.js +22 -4
  255. package/dist/ui/components/ListingLayout.d.ts +1 -2
  256. package/dist/ui/components/ListingLayout.js +1 -1
  257. package/dist/ui/components/RecordDetailModal.d.ts +68 -0
  258. package/dist/ui/components/RecordDetailModal.js +8 -0
  259. package/dist/ui/components/SlottedListingView.js +9 -1
  260. package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
  261. package/dist/ui/forms/FormErrorSummary.js +37 -0
  262. package/dist/ui/forms/FormShell.d.ts +37 -2
  263. package/dist/ui/forms/FormShell.js +37 -7
  264. package/dist/ui/forms/FormShell.style.css +17 -0
  265. package/dist/ui/forms/index.d.ts +2 -0
  266. package/dist/ui/forms/index.js +1 -0
  267. package/dist/ui/index.d.ts +4 -0
  268. package/dist/ui/index.js +5 -0
  269. package/dist/utils/media-url.d.ts +3 -1
  270. package/dist/utils/media-url.js +20 -1
  271. package/dist/validation/schemas.d.ts +4 -4
  272. package/package.json +1 -1
  273. package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
  274. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
  275. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
  276. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
  277. package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
  278. package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
  279. package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
  280. package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
  281. package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
  282. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
  283. package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
  284. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
  285. package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
  286. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
  287. package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
  288. package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
  289. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
  290. package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
  291. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
  292. package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
  293. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
  294. package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
  295. package/dist/features/auth/consent-otp.d.ts +0 -57
  296. package/dist/features/auth/consent-otp.js +0 -127
  297. package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
  298. package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
  299. package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
  300. package/dist/features/checkout/actions/checkout-actions.js +0 -124
  301. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
  302. package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
  303. package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
  304. package/dist/features/classified/components/ClassifiedListView.js +0 -44
  305. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
  306. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
  307. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
  308. package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
  309. package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
  310. package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
  311. package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
  312. package/dist/features/live/components/LiveItemsListView.js +0 -44
  313. package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
  314. package/dist/features/seller/components/SellerTemplatesView.js +0 -276
  315. package/dist/react/hooks/useVisibleItems.d.ts +0 -37
  316. package/dist/react/hooks/useVisibleItems.js +0 -59
  317. package/dist/ui/components/TabStrip.d.ts +0 -34
  318. package/dist/ui/components/TabStrip.js +0 -56
  319. package/dist/ui/components/TabStrip.style.css +0 -97
@@ -5,7 +5,7 @@ import React from "react";
5
5
  import { useQuery } from "@tanstack/react-query";
6
6
  import { z } from "zod";
7
7
  import { Button, ConfirmDeleteModal, Div, Form, Input, Row, Stack, StackedViewShell, Text, Toggle, useToast, } from "../../../ui";
8
- import { FieldInput, FieldTextarea } from "../../../ui/forms";
8
+ import { FieldInput, FieldTextarea, FormErrorSummary } from "../../../ui/forms";
9
9
  import { apiClient } from "../../../http";
10
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
11
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
@@ -119,7 +119,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
119
119
  });
120
120
  const isSubmitting = saveMutation.isPending || itemQuery.isLoading;
121
121
  const canSave = Boolean(groupLabel.trim()) && Boolean(pageLabel.trim()) && Boolean(label.trim());
122
- const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsx(Input, { label: "Phase", value: String(phase), onChange: (e) => setPhase(Math.max(1, parseInt(e.target.value, 10) || 1)), type: "number", min: 1, helperText: "Test batch this case belongs to \u2014 testers work through one phase at a time." })] }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
122
+ const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsx(Input, { label: "Phase", value: String(phase), onChange: (e) => setPhase(Math.max(1, parseInt(e.target.value, 10) || 1)), type: "number", min: 1, helperText: "Test batch this case belongs to \u2014 testers work through one phase at a time." })] }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
123
123
  clearErrors();
124
124
  if (!label.trim()) {
125
125
  setFieldError("label", "Test case label is required");
@@ -11,8 +11,8 @@ export interface AdminUserEditorViewProps {
11
11
  onClose: () => void;
12
12
  userId?: string;
13
13
  displayName?: string;
14
+ photoURL?: string | null;
14
15
  currentRole?: string;
15
- currentIsDisabled?: boolean;
16
16
  currentEmailVerified?: boolean;
17
17
  /** Tester program flag — orthogonal to role. Grants access to the Tester Hub and auto-approves the user's store. */
18
18
  currentIsTester?: boolean;
@@ -37,5 +37,5 @@ export interface AdminUserEditorViewProps {
37
37
  linkedin?: string;
38
38
  };
39
39
  }
40
- export declare function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled: _currentIsDisabled, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }: AdminUserEditorViewProps): React.JSX.Element;
40
+ export declare function AdminUserEditorView({ open, onClose, userId, displayName, photoURL, currentRole, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }: AdminUserEditorViewProps): React.JSX.Element;
41
41
  export {};
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { Code, useApiMutation } from "@mohasinac/appkit/client";
4
4
  import React from "react";
5
5
  import { useQueryClient } from "@tanstack/react-query";
6
- import { Button, ConfirmDeleteModal, Div, Form, FormActions, Heading, Input, Row, Select, SideDrawer, Span, Stack, StackedViewShell, Text, Textarea, Toggle, useToast } from "../../../ui";
6
+ import { Avatar, Button, ConfirmDeleteModal, Div, Form, FormActions, Heading, Input, Row, Select, SideDrawer, Span, Stack, StackedViewShell, Text, Textarea, Toggle, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  const ROLE_OPTIONS = [
@@ -41,7 +41,7 @@ function SoftBanPanel({ userId, softBans, showAddSoftBan, setShowAddSoftBan, sof
41
41
  return (_jsxs(Div, { surface: "muted", padding: "sm", rounded: "lg", border: "default", children: [_jsxs(Row, { justify: "between", gap: "sm", className: "mb-2", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", 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(Stack, { as: "ul", gap: "xs", className: "mb-3", children: softBans.map((ban) => (_jsxs(Row, { as: "li", align: "start", justify: "between", gap: "xs", surface: "default", padding: "inlineSm", rounded: "md", border: "default", children: [_jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Span, { size: "xs", weight: "semibold", children: formatBanAction(ban.action) }), _jsx(Span, { size: "xs", color: "muted", children: ban.reason }), _jsx(Span, { size: "xs", color: "muted", 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(Stack, { gap: "xs", children: [_jsx(Select, { label: "Action to restrict", options: BANNED_ACTION_OPTIONS, value: softBanAction, onValueChange: setSoftBanAction }), _jsx(Textarea, { label: "Reason (required)", value: softBanReason, onChange: (e) => setSoftBanReason(e.target.value), rows: 2, placeholder: "e.g. Suspicious bid activity\u2026" }), _jsx(Input, { label: "Expires at (optional \u2014 leave blank for permanent)", type: "datetime-local", value: softBanExpiry, onChange: (e) => setSoftBanExpiry(e.target.value) }), _jsxs(Row, { gap: "xs", 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, { size: "xs", color: "muted", children: "No active soft bans." }))] }));
42
42
  }
43
43
  // --- Component ---------------------------------------------------------------
44
- export function AdminUserEditorView({ open, onClose, userId, displayName, currentRole, currentIsDisabled: _currentIsDisabled, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }) {
44
+ export function AdminUserEditorView({ open, onClose, userId, displayName, photoURL, currentRole, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }) {
45
45
  const queryClient = useQueryClient();
46
46
  const { showToast } = useToast();
47
47
  // --- General fields -------------------------------------------------------
@@ -224,6 +224,7 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
224
224
  });
225
225
  const isHardBanned = currentIsHardBanned ?? false;
226
226
  const softBans = currentSoftBans ?? [];
227
+ const renderAvatarHeader = () => userId ? (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Avatar, { src: photoURL ?? null, name: displayName ?? userId, size: "md" }), _jsx(Text, { size: "sm", weight: "semibold", children: displayName ?? userId })] })) : null;
227
228
  const renderInfoCard = () => userId ? (_jsx(Div, { textSize: "xs", surface: "muted", rounded: "lg", border: "default", padding: "inlineSm", children: _jsxs(Stack, { color: "primary", gap: "xs", children: [_jsxs(Text, { size: "xs", children: [_jsx(Span, { weight: "semibold", children: "Owner ID (Firebase UID):" }), " ", _jsx(Code, { className: "select-all font-mono", children: userId })] }), ownedStoreId && (_jsxs(Text, { size: "xs", children: [_jsx(Span, { weight: "semibold", children: "Owns store:" }), " ", _jsx(Code, { className: "select-all font-mono", children: ownedStoreId }), ownedStoreName ? ` — ${ownedStoreName}` : ""] }))] }) })) : null;
228
229
  const renderRoleSection = () => (_jsx(Select, { label: "Role", options: ROLE_OPTIONS, value: role, onValueChange: setRole }));
229
230
  const renderEmailVerifiedSection = () => (_jsx(Toggle, { label: "Email verified", checked: emailVerified, onChange: setEmailVerified }));
@@ -240,6 +241,7 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
240
241
  e.preventDefault();
241
242
  saveMutation.mutate();
242
243
  }, padding: "md", children: _jsx(StackedViewShell, { portal: "admin", className: "space-y-4", sections: [
244
+ renderAvatarHeader,
243
245
  renderInfoCard,
244
246
  renderRoleSection,
245
247
  renderEmailVerifiedSection,
@@ -215,7 +215,7 @@ export function AdminUsersView({ children, ...props }) {
215
215
  },
216
216
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_USER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Role", tabs: ADMIN_USER_ROLE_TABS, value: pendingFilters.role ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, role: id })) })] })),
217
217
  };
218
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), currentIsTester: Boolean(selectedRow?._raw?.isTester), currentCanTestAdmin: Boolean(selectedRow?._raw?.canTestAdmin), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
218
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, photoURL: toStringValue(selectedRow?._raw?.photoURL, "") || undefined, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), currentIsTester: Boolean(selectedRow?._raw?.isTester), currentCanTestAdmin: Boolean(selectedRow?._raw?.canTestAdmin), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
219
219
  selectedRow?._raw?.hardBanReason), currentHardBanReason: toStringValue(selectedRow?._raw?.hardBanReason, "") || undefined, currentSoftBans: Array.isArray(selectedRow?._raw?.softBans)
220
220
  ? selectedRow._raw.softBans.map((b) => ({
221
221
  action: toStringValue(b.action, ""),
@@ -2,8 +2,10 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { sortBy } from "@mohasinac/appkit/client";
4
4
  import React from "react";
5
+ import { useRouter } from "next/navigation";
5
6
  import { ListingLayout } from "../../../ui";
6
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
+ import { ROUTES } from "../../../next/routing/route-map";
7
9
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
8
10
  import { DataListingView } from "./DataListingView";
9
11
  const ADMIN_WISHLISTS_CONFIG = {
@@ -29,14 +31,25 @@ const ADMIN_WISHLISTS_CONFIG = {
29
31
  secondary: `${itemCount} item${itemCount === 1 ? "" : "s"} of ${limit}`,
30
32
  status: isFull ? "Full" : itemCount >= limit - 2 ? "Near cap" : "OK",
31
33
  updatedAt: toRelativeDate(item.updatedAt),
34
+ userId: toStringValue(item.userId, ""),
32
35
  };
33
36
  }),
34
37
  getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
35
38
  buildFilters: () => undefined,
36
39
  };
37
40
  export function AdminWishlistsView({ children, ...props }) {
41
+ const router = useRouter();
38
42
  if (React.Children.count(children) > 0) {
39
43
  return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
40
44
  }
41
- return _jsx(DataListingView, { config: ADMIN_WISHLISTS_CONFIG });
45
+ // The list API (`wishlistRepository.findAllSummaries()`) deliberately
46
+ // returns only {userId, itemCount, isFull, updatedAt} — never the items
47
+ // themselves, since pulling every user's ≤20 items into one payload is
48
+ // exactly the unbounded-response shape Rule #6 forbids. So the row already
49
+ // shows its whole record, and the genuinely useful destination is the
50
+ // owning user's admin page, which renders their real detail.
51
+ return (_jsx(DataListingView, { config: {
52
+ ...ADMIN_WISHLISTS_CONFIG,
53
+ onRowClick: (row) => row.userId && router.push(String(ROUTES.ADMIN.USER_DETAIL(row.userId))),
54
+ } }));
42
55
  }
@@ -74,8 +74,8 @@ export interface ListingSelectionContext<TRow extends {
74
74
  export interface ListingViewConfig<TResponse, TRow extends {
75
75
  id: string;
76
76
  }> extends AdminListingConfig<TResponse, TRow> {
77
- /** Portal context — currently admin/seller share the same chrome. */
78
- portal: "admin" | "seller";
77
+ /** Portal context — admin/seller/user all share the same chrome. */
78
+ portal: "admin" | "seller" | "user";
79
79
  title: string;
80
80
  subtitle?: string;
81
81
  searchPlaceholder: string;
@@ -0,0 +1,19 @@
1
+ import type { JsonValue } from "../../../schemas/types";
2
+ export interface AuditLogEntryDetail {
3
+ id: string;
4
+ actorUid: string;
5
+ actorName?: string;
6
+ action: string;
7
+ targetType: string;
8
+ targetId: string;
9
+ targetLabel?: string;
10
+ reason?: string;
11
+ metadata?: Record<string, JsonValue>;
12
+ createdAt?: string;
13
+ }
14
+ export interface ViewAuditLogEntryModalProps {
15
+ entry: AuditLogEntryDetail | null;
16
+ isOpen: boolean;
17
+ onClose: () => void;
18
+ }
19
+ export declare function ViewAuditLogEntryModal({ entry, isOpen, onClose }: ViewAuditLogEntryModalProps): import("react").JSX.Element | null;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * ViewAuditLogEntryModal — full audit-log entry details in a Modal overlay.
5
+ * Mirrors ViewReviewModal's shape (appkit/src/features/reviews/components/ReviewModal.tsx).
6
+ */
7
+ import { Badge, Code, Div, Modal, Row, Stack, Text } from "../../../ui";
8
+ const ACTION_LABELS = {
9
+ user_hard_ban: "User hard-banned",
10
+ user_soft_ban: "User soft-banned",
11
+ user_unban: "User unbanned",
12
+ checkout_bypass: "Admin checkout bypass",
13
+ coupon_update: "Coupon updated",
14
+ payout_mark_paid: "Payout marked paid",
15
+ store_status_change: "Store status changed",
16
+ user_role_change: "User role changed",
17
+ };
18
+ export function ViewAuditLogEntryModal({ entry, isOpen, onClose }) {
19
+ if (!entry)
20
+ return null;
21
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Audit Log Entry", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Badge, { variant: "default", children: ACTION_LABELS[entry.action] ?? entry.action }), entry.createdAt && (_jsx(Text, { size: "xs", color: "muted", children: new Date(entry.createdAt).toLocaleString() }))] }), _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Actor" }), _jsx(Text, { size: "sm", weight: "medium", children: entry.actorName ?? entry.actorUid })] }), _jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Target" }), _jsxs(Text, { size: "sm", weight: "medium", children: [entry.targetType, ": ", entry.targetLabel ?? entry.targetId] })] })] }), entry.reason && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", children: "Reason" }), _jsx(Text, { size: "sm", children: entry.reason })] })), entry.metadata && Object.keys(entry.metadata).length > 0 && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", className: "mb-1", children: "Metadata" }), _jsx(Code, { size: "xs", className: "block whitespace-pre-wrap font-mono", children: JSON.stringify(entry.metadata, null, 2) })] }))] }) }));
22
+ }
@@ -0,0 +1,21 @@
1
+ export interface NotificationEntryDetail {
2
+ id: string;
3
+ userId: string;
4
+ type: string;
5
+ priority?: string;
6
+ title: string;
7
+ message: string;
8
+ imageUrl?: string;
9
+ actionUrl?: string;
10
+ actionLabel?: string;
11
+ isRead: boolean;
12
+ relatedId?: string;
13
+ relatedType?: string;
14
+ createdAt?: string;
15
+ }
16
+ export interface ViewNotificationModalProps {
17
+ notification: NotificationEntryDetail | null;
18
+ isOpen: boolean;
19
+ onClose: () => void;
20
+ }
21
+ export declare function ViewNotificationModal({ notification, isOpen, onClose }: ViewNotificationModalProps): import("react").JSX.Element | null;
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * ViewNotificationModal — full notification details in a Modal overlay.
5
+ * Mirrors ViewReviewModal's shape (appkit/src/features/reviews/components/ReviewModal.tsx).
6
+ * AdminNotificationsView was previously list-only — no way to see a
7
+ * notification's full body/payload/link, only Resend/Delete row actions.
8
+ */
9
+ import { Anchor, Badge, Div, Modal, Row, Stack, Text } from "../../../ui";
10
+ import { MediaImage } from "../../media/MediaImage";
11
+ export function ViewNotificationModal({ notification, isOpen, onClose }) {
12
+ if (!notification)
13
+ return null;
14
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Notification Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Badge, { variant: "default", children: notification.type }), _jsx(Badge, { variant: notification.isRead ? "default" : "info", children: notification.isRead ? "Read" : "Unread" })] }), notification.createdAt && (_jsx(Text, { size: "xs", color: "muted", children: new Date(notification.createdAt).toLocaleString() }))] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "lg", weight: "semibold", children: notification.title }), _jsx(Text, { size: "sm", color: "muted", children: notification.message })] }), notification.imageUrl && (_jsx(Div, { className: "h-32 w-full", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: notification.imageUrl, alt: notification.title, size: "card" }) })), _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Recipient" }), _jsx(Text, { size: "sm", weight: "medium", children: notification.userId })] }), notification.relatedType && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Related to" }), _jsxs(Text, { size: "sm", weight: "medium", children: [notification.relatedType, ": ", notification.relatedId] })] }))] }), notification.actionUrl && (_jsx(Row, { justify: "end", children: _jsxs(Anchor, { href: notification.actionUrl, tone: "brand", weight: "semibold", size: "sm", children: [notification.actionLabel ?? "Open link", " \u2192"] }) }))] }) }));
15
+ }
@@ -1,8 +1,5 @@
1
- export interface AdminPageViewsReportViewProps {
2
- startDate: string;
3
- endDate: string;
4
- }
5
1
  /** Admin analytics tab — page views for the requested date range, grouped by
6
- * entity (product/store/category/homepage/auction/review/user-profile) and
7
- * by raw URL. Backed by the day-bucketed pageViews collection. */
8
- export declare function AdminPageViewsReportView({ startDate, endDate }: AdminPageViewsReportViewProps): import("react").JSX.Element;
2
+ * entity (see PAGE_VIEW_ENTITY_TYPES for the full list), searchable by
3
+ * entity/URL, filterable by entity type, sortable by view count, and
4
+ * paginated. Backed by the day-bucketed pageViews collection. */
5
+ export declare function AdminPageViewsReportView(): import("react").JSX.Element;
@@ -1,41 +1,61 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useQuery } from "@tanstack/react-query";
4
- import { Alert, Div, Stack, Text } from "../../../../ui";
5
- import { DataTable } from "../DataTable";
6
- import { apiClient } from "../../../../http";
3
+ import { useState } from "react";
4
+ import { SIEVE_OP, sieveFilter, sortBy } from "@mohasinac/appkit/client";
5
+ import { Label, Input, PaginatedSelect } from "../../../../ui";
6
+ import { DataListingView } from "../DataListingView";
7
7
  import { ADMIN_ENDPOINTS } from "../../../../constants/api-endpoints";
8
+ import { PAGE_VIEW_ENTITY_TYPES } from "../../../analytics/types";
9
+ const ENTITY_TYPE_OPTIONS = PAGE_VIEW_ENTITY_TYPES.map((t) => ({ value: t, label: t }));
10
+ function today() {
11
+ // eslint-disable-next-line lir/no-raw-date
12
+ return new Date().toISOString().slice(0, 10);
13
+ }
14
+ function daysAgo(n) {
15
+ // eslint-disable-next-line lir/no-raw-date
16
+ const d = new Date();
17
+ d.setDate(d.getDate() - n);
18
+ return d.toISOString().slice(0, 10);
19
+ }
20
+ const COLUMNS = [
21
+ { key: "entityType", header: "Type" },
22
+ { key: "entityId", header: "Entity" },
23
+ { key: "url", header: "URL" },
24
+ { key: "count", header: "Views", sortable: true },
25
+ ];
8
26
  /** Admin analytics tab — page views for the requested date range, grouped by
9
- * entity (product/store/category/homepage/auction/review/user-profile) and
10
- * by raw URL. Backed by the day-bucketed pageViews collection. */
11
- export function AdminPageViewsReportView({ startDate, endDate }) {
12
- const endpoint = `${ADMIN_ENDPOINTS.ANALYTICS_PAGE_VIEWS}?startDate=${startDate}&endDate=${endDate}`;
13
- const query = useQuery({
14
- queryKey: ["admin-page-views", endpoint],
15
- queryFn: () => apiClient.get(endpoint),
16
- staleTime: 60000,
17
- });
18
- if (query.error) {
19
- return (_jsx(Alert, { variant: "error", title: "Could not load page views", children: query.error instanceof Error ? query.error.message : "Unknown error" }));
20
- }
21
- const data = query.data;
22
- return (_jsxs(Stack, { gap: "lg", children: [_jsx(Text, { size: "sm", color: "muted", children: data ? `${data.total.toLocaleString()} total views, ${startDate} – ${endDate}` : "Loading…" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "semibold", className: "mb-2", children: "By entity" }), _jsx(DataTable, { columns: [
23
- { key: "entityType", header: "Type" },
24
- { key: "entityId", header: "Entity" },
25
- { key: "url", header: "URL" },
26
- { key: "count", header: "Views" },
27
- ], rows: (data?.byEntity ?? []).map((row) => ({
28
- id: `${row.entityType}:${row.entityId}`,
29
- entityType: row.entityType,
30
- entityId: row.entityId,
31
- url: row.url,
32
- count: row.count.toLocaleString(),
33
- })), isLoading: query.isLoading, emptyLabel: "No page views recorded for this range." })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "semibold", className: "mb-2", children: "By URL" }), _jsx(DataTable, { columns: [
34
- { key: "url", header: "URL" },
35
- { key: "count", header: "Views" },
36
- ], rows: (data?.byUrl ?? []).map((row) => ({
37
- id: row.url,
38
- url: row.url,
39
- count: row.count.toLocaleString(),
40
- })), isLoading: query.isLoading, emptyLabel: "No page views recorded for this range." })] })] }));
27
+ * entity (see PAGE_VIEW_ENTITY_TYPES for the full list), searchable by
28
+ * entity/URL, filterable by entity type, sortable by view count, and
29
+ * paginated. Backed by the day-bucketed pageViews collection. */
30
+ export function AdminPageViewsReportView() {
31
+ const [startDate, setStartDate] = useState(daysAgo(30));
32
+ const [endDate, setEndDate] = useState(today());
33
+ const config = {
34
+ portal: "admin",
35
+ title: "Page Views",
36
+ searchPlaceholder: "Search by entity or URL",
37
+ emptyLabel: "No page views recorded for this range.",
38
+ filterKeys: ["entityType"],
39
+ defaultSort: sortBy("count", "DESC"),
40
+ queryKey: ["admin", "analytics", "pageviews", "listing", startDate, endDate],
41
+ endpoint: `${ADMIN_ENDPOINTS.ANALYTICS_PAGE_VIEWS}?startDate=${startDate}&endDate=${endDate}`,
42
+ sortOptions: [
43
+ { value: sortBy("count", "DESC"), label: "Most views" },
44
+ { value: sortBy("count", "ASC"), label: "Fewest views" },
45
+ ],
46
+ columns: COLUMNS,
47
+ mapRows: (response) => response.items.map((item) => ({
48
+ id: `${item.entityType}:${item.entityId}`,
49
+ entityType: item.entityType,
50
+ entityId: item.entityId,
51
+ url: item.url,
52
+ count: item.count,
53
+ })),
54
+ getTotal: (response) => response.total,
55
+ buildFilters: (state) => state.entityType ? sieveFilter("entityType", SIEVE_OP.EQ, state.entityType) : undefined,
56
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(PaginatedSelect, { value: pendingFilters.entityType || undefined, onChange: (v) => setPendingFilters((p) => ({ ...p, entityType: v ?? "" })), options: ENTITY_TYPE_OPTIONS })),
57
+ rowHrefTemplate: (row) => row.url,
58
+ renderAboveContent: () => (_jsxs(Label, { layout: "flex", gap: "md", color: "muted", size: "sm", className: "px-[var(--appkit-space-3)] pb-[var(--appkit-space-2)]", children: [_jsx(Input, { type: "date", value: startDate, max: endDate, onChange: (e) => setStartDate(e.target.value), className: "rounded-lg border px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text)]" }), "to", _jsx(Input, { type: "date", value: endDate, min: startDate, max: today(), onChange: (e) => setEndDate(e.target.value), className: "rounded-lg border px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text)]" })] })),
59
+ };
60
+ return _jsx(DataListingView, { config: config });
41
61
  }
@@ -97,6 +97,14 @@ export { AdminHistoryView } from "./AdminHistoryView";
97
97
  export type { AdminHistoryViewProps } from "./AdminHistoryView";
98
98
  export { AdminPayoutsView } from "./AdminPayoutsView";
99
99
  export type { AdminPayoutsViewProps } from "./AdminPayoutsView";
100
+ export { AdminPayoutMarkPaidModal } from "./AdminPayoutMarkPaidModal";
101
+ export type { AdminPayoutMarkPaidModalProps } from "./AdminPayoutMarkPaidModal";
102
+ export { AdminAuditLogView } from "./AdminAuditLogView";
103
+ export type { AdminAuditLogViewProps } from "./AdminAuditLogView";
104
+ export { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
105
+ export type { AuditLogEntryDetail, ViewAuditLogEntryModalProps } from "./ViewAuditLogEntryModal";
106
+ export { ViewNotificationModal } from "./ViewNotificationModal";
107
+ export type { NotificationEntryDetail, ViewNotificationModalProps } from "./ViewNotificationModal";
100
108
  export { AdminReviewsView } from "./AdminReviewsView";
101
109
  export type { AdminReviewsViewProps } from "./AdminReviewsView";
102
110
  export { AdminSectionsView } from "./AdminSectionsView";
@@ -134,7 +142,6 @@ export type { AdminRevenueChartProps, AdminOrdersChartProps, } from "./analytics
134
142
  export { AdminTopProductsTable } from "./analytics/AdminTopProductsTable";
135
143
  export type { AdminTopProductsTableProps, AdminTopProductsTableLabels, } from "./analytics/AdminTopProductsTable";
136
144
  export { AdminPageViewsReportView } from "./analytics/AdminPageViewsReportView";
137
- export type { AdminPageViewsReportViewProps } from "./analytics/AdminPageViewsReportView";
138
145
  export { AdminFeatureFlagsView } from "./AdminFeatureFlagsView";
139
146
  export type { AdminFeatureFlagsViewProps } from "./AdminFeatureFlagsView";
140
147
  export { AdminNewsletterView } from "./AdminNewsletterView";
@@ -53,6 +53,10 @@ export { AdminCartsView } from "./AdminCartsView";
53
53
  export { AdminWishlistsView } from "./AdminWishlistsView";
54
54
  export { AdminHistoryView } from "./AdminHistoryView";
55
55
  export { AdminPayoutsView } from "./AdminPayoutsView";
56
+ export { AdminPayoutMarkPaidModal } from "./AdminPayoutMarkPaidModal";
57
+ export { AdminAuditLogView } from "./AdminAuditLogView";
58
+ export { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
59
+ export { ViewNotificationModal } from "./ViewNotificationModal";
56
60
  export { AdminReviewsView } from "./AdminReviewsView";
57
61
  export { AdminSectionsView } from "./AdminSectionsView";
58
62
  export { AdminSessionsManager } from "./AdminSessionsManager";
@@ -148,6 +148,36 @@ export declare const ADMIN_PAYOUT_STATUS_TABS: readonly [{
148
148
  readonly id: "failed";
149
149
  readonly label: "Failed";
150
150
  }];
151
+ /** Admin > Audit Log — action-type filter chip set. IDs match
152
+ * `AdminAuditActionValues` (`appkit/src/features/audit-log/schemas/firestore.ts`) exactly. */
153
+ export declare const ADMIN_AUDIT_LOG_ACTION_TABS: readonly [{
154
+ readonly id: "All";
155
+ readonly label: "All";
156
+ }, {
157
+ readonly id: "user_hard_ban";
158
+ readonly label: "Hard ban";
159
+ }, {
160
+ readonly id: "user_soft_ban";
161
+ readonly label: "Soft ban";
162
+ }, {
163
+ readonly id: "user_unban";
164
+ readonly label: "Unban";
165
+ }, {
166
+ readonly id: "checkout_bypass";
167
+ readonly label: "Checkout bypass";
168
+ }, {
169
+ readonly id: "coupon_update";
170
+ readonly label: "Coupon update";
171
+ }, {
172
+ readonly id: "payout_mark_paid";
173
+ readonly label: "Payout paid";
174
+ }, {
175
+ readonly id: "store_status_change";
176
+ readonly label: "Store status";
177
+ }, {
178
+ readonly id: "user_role_change";
179
+ readonly label: "Role change";
180
+ }];
151
181
  /** Admin > Orders — order-state filter chip set. Lowercase IDs match
152
182
  * `OrderStatusValues` and the actual Firestore document `status` field. */
153
183
  export declare const ADMIN_ORDER_STATUS_TABS: readonly [{
@@ -175,6 +205,25 @@ export declare const ADMIN_ORDER_STATUS_TABS: readonly [{
175
205
  readonly id: "return_requested";
176
206
  readonly label: "Return Requested";
177
207
  }];
208
+ /**
209
+ * Admin > Orders — manual-payment (cash / UPI / EMI) review queue.
210
+ *
211
+ * Deliberately NOT a `status` chip set: these two states are *derived* from
212
+ * `paymentProofUrl` + `paymentReviewOutcome`, both of which are absent-by-
213
+ * default fields that no Firestore equality filter can select on. The IDs are
214
+ * `PaymentReviewQueueMode` values and are sent as the `paymentReview` query
215
+ * param (not a Sieve filter) — see `orderRepository.listPaymentReviewQueue`.
216
+ */
217
+ export declare const ADMIN_ORDER_PAYMENT_REVIEW_TABS: readonly [{
218
+ readonly id: "All";
219
+ readonly label: "All";
220
+ }, {
221
+ readonly id: "awaiting_proof";
222
+ readonly label: "Awaiting payment";
223
+ }, {
224
+ readonly id: "awaiting_verification";
225
+ readonly label: "Awaiting verification";
226
+ }];
178
227
  /** Admin > Reviews — moderation state filter chip set. */
179
228
  export declare const ADMIN_REVIEW_STATUS_TABS: readonly [{
180
229
  readonly id: "All";
@@ -74,6 +74,19 @@ export const ADMIN_PAYOUT_STATUS_TABS = [
74
74
  { id: "paid", label: "Paid" },
75
75
  { id: "failed", label: "Failed" },
76
76
  ];
77
+ /** Admin > Audit Log — action-type filter chip set. IDs match
78
+ * `AdminAuditActionValues` (`appkit/src/features/audit-log/schemas/firestore.ts`) exactly. */
79
+ export const ADMIN_AUDIT_LOG_ACTION_TABS = [
80
+ ALL_TAB,
81
+ { id: "user_hard_ban", label: "Hard ban" },
82
+ { id: "user_soft_ban", label: "Soft ban" },
83
+ { id: "user_unban", label: "Unban" },
84
+ { id: "checkout_bypass", label: "Checkout bypass" },
85
+ { id: "coupon_update", label: "Coupon update" },
86
+ { id: "payout_mark_paid", label: "Payout paid" },
87
+ { id: "store_status_change", label: "Store status" },
88
+ { id: "user_role_change", label: "Role change" },
89
+ ];
77
90
  /** Admin > Orders — order-state filter chip set. Lowercase IDs match
78
91
  * `OrderStatusValues` and the actual Firestore document `status` field. */
79
92
  export const ADMIN_ORDER_STATUS_TABS = [
@@ -86,6 +99,20 @@ export const ADMIN_ORDER_STATUS_TABS = [
86
99
  { id: "refunded", label: "Refunded" },
87
100
  { id: "return_requested", label: "Return Requested" },
88
101
  ];
102
+ /**
103
+ * Admin > Orders — manual-payment (cash / UPI / EMI) review queue.
104
+ *
105
+ * Deliberately NOT a `status` chip set: these two states are *derived* from
106
+ * `paymentProofUrl` + `paymentReviewOutcome`, both of which are absent-by-
107
+ * default fields that no Firestore equality filter can select on. The IDs are
108
+ * `PaymentReviewQueueMode` values and are sent as the `paymentReview` query
109
+ * param (not a Sieve filter) — see `orderRepository.listPaymentReviewQueue`.
110
+ */
111
+ export const ADMIN_ORDER_PAYMENT_REVIEW_TABS = [
112
+ ALL_TAB,
113
+ { id: "awaiting_proof", label: "Awaiting payment" },
114
+ { id: "awaiting_verification", label: "Awaiting verification" },
115
+ ];
89
116
  /** Admin > Reviews — moderation state filter chip set. */
90
117
  export const ADMIN_REVIEW_STATUS_TABS = [
91
118
  ALL_TAB,
@@ -9,6 +9,12 @@ export interface AdminListingConfig<TResponse, TRow extends {
9
9
  mapRows: (response: TResponse) => TRow[];
10
10
  getTotal?: (response: TResponse, rows: TRow[]) => number;
11
11
  buildFilters: (filterState: Record<string, string>) => string | undefined;
12
+ /**
13
+ * Extra query params derived from the same filter state, for endpoints whose
14
+ * filter can't be expressed as a Sieve string. Use `buildFilters` for
15
+ * anything Sieve *can* express — this is the escape hatch, not the default.
16
+ */
17
+ buildExtraParams?: (filterState: Record<string, string>) => Record<string, string | undefined> | undefined;
12
18
  initialView?: "grid" | "list" | "table";
13
19
  /** Values used for filterKeys absent from the URL — lets a view default to
14
20
  * a non-"All" filter state (e.g. hide inactive rows) on first load, while
@@ -6,7 +6,7 @@ import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
6
6
  import { useDataViewMode } from "../../account/hooks/useDataViewMode";
7
7
  import { useAdminListingData } from "./useAdminListingData";
8
8
  export function useAdminListing(config) {
9
- const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, filterDefaults = {}, } = config;
9
+ const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, buildExtraParams, filterDefaults = {}, } = config;
10
10
  // Persisted, viewport-aware view-mode: below 768px defaults to "list"
11
11
  // (AdminViewCards' one-full-width-card-per-row layout) unless the caller
12
12
  // explicitly pins hideTableView (grid-only views like coupons); the user's
@@ -52,6 +52,7 @@ export function useAdminListing(config) {
52
52
  const hasActiveState = !!table.get("q") || table.get("sort") !== defaultSort || activeFilterCount > 0;
53
53
  const currentFilterState = Object.fromEntries(filterKeys.map((k) => [k, table.get(k)]));
54
54
  const filters = buildFilters(currentFilterState);
55
+ const extraParams = buildExtraParams?.(currentFilterState);
55
56
  const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
56
57
  queryKey,
57
58
  endpoint,
@@ -60,6 +61,7 @@ export function useAdminListing(config) {
60
61
  sorts: table.get("sort") || defaultSort,
61
62
  filters,
62
63
  q: table.get("q") || undefined,
64
+ extraParams,
63
65
  mapRows,
64
66
  getTotal,
65
67
  });
@@ -15,6 +15,14 @@ interface UseAdminListingDataOptions<TResponse, TRow extends {
15
15
  sorts?: string;
16
16
  filters?: string;
17
17
  q?: string;
18
+ /**
19
+ * Extra query-string params appended verbatim, for listing endpoints whose
20
+ * filter isn't expressible as a Sieve string (e.g. the admin orders
21
+ * payment-review queue, which keys off the *absence* of a field). Keys with
22
+ * an `undefined` value are dropped. Participates in the react-query key, so
23
+ * changing one refetches rather than serving another mode's cached page.
24
+ */
25
+ extraParams?: Record<string, string | undefined>;
18
26
  mapRows: (response: TResponse) => TRow[];
19
27
  getTotal?: (response: TResponse, rows: TRow[]) => number;
20
28
  }
@@ -29,7 +37,7 @@ export interface UseAdminListingDataResult<TRow extends {
29
37
  }
30
38
  export declare function useAdminListingData<TResponse, TRow extends {
31
39
  id: string;
32
- }>({ queryKey, endpoint, page, pageSize, sorts, filters, q, mapRows, getTotal, }: UseAdminListingDataOptions<TResponse, TRow>): UseAdminListingDataResult<TRow>;
40
+ }>({ queryKey, endpoint, page, pageSize, sorts, filters, q, extraParams, mapRows, getTotal, }: UseAdminListingDataOptions<TResponse, TRow>): UseAdminListingDataResult<TRow>;
33
41
  export declare function toRecordArray(value: unknown): ListingItemRecord[];
34
42
  export declare function toStringValue(value: unknown, fallback?: string): string;
35
43
  export declare function toCurrency(value: unknown): string;
@@ -7,7 +7,7 @@ function withQueryParams(endpoint, params) {
7
7
  const query = new URLSearchParams(params).toString();
8
8
  return `${endpoint}${hasQuery ? "&" : "?"}${query}`;
9
9
  }
10
- export function useAdminListingData({ queryKey, endpoint, page = 1, pageSize = 25, sorts = "-createdAt", filters, q, mapRows, getTotal, }) {
10
+ export function useAdminListingData({ queryKey, endpoint, page = 1, pageSize = 25, sorts = "-createdAt", filters, q, extraParams, mapRows, getTotal, }) {
11
11
  const params = {
12
12
  page: String(page),
13
13
  pageSize: String(pageSize),
@@ -17,8 +17,15 @@ export function useAdminListingData({ queryKey, endpoint, page = 1, pageSize = 2
17
17
  params.filters = filters;
18
18
  if (q)
19
19
  params.q = q;
20
+ const extraEntries = Object.entries(extraParams ?? {})
21
+ .filter((entry) => entry[1] !== undefined && entry[1] !== "")
22
+ .sort(([a], [b]) => a.localeCompare(b));
23
+ for (const [key, value] of extraEntries)
24
+ params[key] = value;
20
25
  const query = useQuery({
21
- queryKey: [...queryKey, page, pageSize, sorts, filters ?? "", q ?? ""],
26
+ // extraEntries is sorted so an identical param set always produces an
27
+ // identical key regardless of object insertion order.
28
+ queryKey: [...queryKey, page, pageSize, sorts, filters ?? "", q ?? "", extraEntries],
22
29
  queryFn: () => apiClient.get(withQueryParams(endpoint, params)),
23
30
  staleTime: 60000,
24
31
  });
@@ -103,6 +103,11 @@ export declare class SiteSettingsRepository extends BaseRepository<SiteSettingsD
103
103
  fromName: string;
104
104
  fromEmail: string;
105
105
  replyTo: string;
106
+ dailyDigest?: {
107
+ enabled: boolean;
108
+ recipients: string[];
109
+ ccRecipients: string[];
110
+ };
106
111
  }>;
107
112
  /**
108
113
  * Check if site settings exist