@mohasinac/appkit 2.7.27 → 2.7.31

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 (397) hide show
  1. package/dist/_internal/client/features/filters/filter-load-options.d.ts +29 -0
  2. package/dist/_internal/client/features/filters/filter-load-options.js +98 -0
  3. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  4. package/dist/_internal/server/features/checkout/actions.js +42 -35
  5. package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
  6. package/dist/_internal/server/features/classified/adapters.js +24 -0
  7. package/dist/_internal/server/features/classified/index.d.ts +3 -0
  8. package/dist/_internal/server/features/classified/index.js +3 -0
  9. package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
  10. package/dist/_internal/server/features/classified/metadata.js +43 -0
  11. package/dist/_internal/server/features/classified/og.d.ts +30 -0
  12. package/dist/_internal/server/features/classified/og.js +76 -0
  13. package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
  14. package/dist/_internal/server/features/digital-code/adapters.js +29 -0
  15. package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
  16. package/dist/_internal/server/features/digital-code/index.js +3 -0
  17. package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
  18. package/dist/_internal/server/features/digital-code/metadata.js +36 -0
  19. package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
  20. package/dist/_internal/server/features/digital-code/og.js +77 -0
  21. package/dist/_internal/server/features/live/adapters.d.ts +21 -0
  22. package/dist/_internal/server/features/live/adapters.js +34 -0
  23. package/dist/_internal/server/features/live/index.d.ts +3 -0
  24. package/dist/_internal/server/features/live/index.js +3 -0
  25. package/dist/_internal/server/features/live/metadata.d.ts +8 -0
  26. package/dist/_internal/server/features/live/metadata.js +39 -0
  27. package/dist/_internal/server/features/live/og.d.ts +27 -0
  28. package/dist/_internal/server/features/live/og.js +72 -0
  29. package/dist/_internal/server/features/promotions/actions.js +4 -3
  30. package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
  31. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
  32. package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
  33. package/dist/_internal/shared/actions/action-registry.js +504 -9
  34. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  35. package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
  36. package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
  37. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  38. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
  39. package/dist/_internal/shared/features/events/schema.d.ts +8 -8
  40. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  41. package/dist/_internal/shared/features/products/schema.d.ts +42 -42
  42. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  43. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  44. package/dist/_internal/shared/media/limits.js +4 -3
  45. package/dist/client.d.ts +4 -0
  46. package/dist/client.js +2 -0
  47. package/dist/configs/next.js +4 -0
  48. package/dist/constants/api-endpoints.d.ts +6 -0
  49. package/dist/constants/api-endpoints.js +2 -0
  50. package/dist/constants/field-names.d.ts +1 -0
  51. package/dist/constants/field-names.js +1 -0
  52. package/dist/constants/table-keys.d.ts +6 -0
  53. package/dist/constants/table-keys.js +9 -0
  54. package/dist/core/hooks/useSyncManager.js +9 -8
  55. package/dist/features/about/components/FeesView.js +10 -7
  56. package/dist/features/about/components/HelpPageView.js +10 -1
  57. package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
  58. package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
  59. package/dist/features/about/components/HowOffersWorkView.js +10 -1
  60. package/dist/features/about/components/HowOrdersWorkView.js +10 -1
  61. package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
  62. package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
  63. package/dist/features/about/components/HowReviewsWorkView.js +10 -1
  64. package/dist/features/about/components/PublicProfileView.js +46 -16
  65. package/dist/features/about/components/SecurityPrivacyView.js +7 -1
  66. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  67. package/dist/features/account/components/NotificationBell.js +27 -16
  68. package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
  69. package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
  70. package/dist/features/account/components/UserSidebar.js +4 -4
  71. package/dist/features/account/components/UserSupportView.js +16 -6
  72. package/dist/features/account/components/index.d.ts +2 -0
  73. package/dist/features/account/components/index.js +1 -0
  74. package/dist/features/account/schemas/index.d.ts +4 -4
  75. package/dist/features/account/types/index.d.ts +34 -0
  76. package/dist/features/admin/actions/admin-actions.js +4 -3
  77. package/dist/features/admin/actions/notification-actions.d.ts +24 -22
  78. package/dist/features/admin/actions/notification-actions.js +122 -22
  79. package/dist/features/admin/components/AdminAdEditorView.js +29 -15
  80. package/dist/features/admin/components/AdminAddressEditorView.d.ts +7 -0
  81. package/dist/features/admin/components/AdminAddressEditorView.js +117 -0
  82. package/dist/features/admin/components/AdminAdsView.js +22 -28
  83. package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
  84. package/dist/features/admin/components/AdminBidsView.js +10 -3
  85. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  86. package/dist/features/admin/components/AdminBlogView.js +8 -3
  87. package/dist/features/admin/components/AdminBrandsView.js +7 -2
  88. package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
  89. package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
  90. package/dist/features/admin/components/AdminCarouselView.js +13 -7
  91. package/dist/features/admin/components/AdminCategoriesView.js +8 -3
  92. package/dist/features/admin/components/AdminCategoryEditorView.js +12 -9
  93. package/dist/features/admin/components/AdminContactView.js +6 -1
  94. package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
  95. package/dist/features/admin/components/AdminCouponsView.js +6 -1
  96. package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
  97. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  98. package/dist/features/admin/components/AdminFaqsView.js +7 -2
  99. package/dist/features/admin/components/AdminFeatureFlagsView.js +58 -13
  100. package/dist/features/admin/components/AdminFeaturesView.js +4 -3
  101. package/dist/features/admin/components/AdminMediaView.js +9 -8
  102. package/dist/features/admin/components/AdminNavigationView.js +10 -17
  103. package/dist/features/admin/components/AdminNewsletterView.js +6 -1
  104. package/dist/features/admin/components/AdminNotificationsView.js +11 -3
  105. package/dist/features/admin/components/AdminOrdersView.js +6 -1
  106. package/dist/features/admin/components/AdminPayoutsView.js +10 -3
  107. package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
  108. package/dist/features/admin/components/AdminProductEditorView.d.ts +3 -1
  109. package/dist/features/admin/components/AdminProductEditorView.js +12 -9
  110. package/dist/features/admin/components/AdminProductsView.js +18 -12
  111. package/dist/features/admin/components/AdminReturnRequestsView.js +5 -4
  112. package/dist/features/admin/components/AdminReviewsView.js +36 -26
  113. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  114. package/dist/features/admin/components/AdminScammersView.js +28 -26
  115. package/dist/features/admin/components/AdminSectionsView.js +112 -99
  116. package/dist/features/admin/components/AdminSessionsView.js +10 -3
  117. package/dist/features/admin/components/AdminSidebar.js +4 -4
  118. package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
  119. package/dist/features/admin/components/AdminStoresView.js +6 -1
  120. package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
  121. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  122. package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
  123. package/dist/features/admin/components/AdminTeamView.js +6 -1
  124. package/dist/features/admin/components/AdminUserEditorView.js +8 -15
  125. package/dist/features/admin/components/AdminUsersView.js +6 -1
  126. package/dist/features/admin/components/AdminViewCards.js +4 -3
  127. package/dist/features/admin/components/DashboardStats.js +2 -2
  128. package/dist/features/admin/components/DataTable.js +2 -2
  129. package/dist/features/admin/components/index.d.ts +2 -0
  130. package/dist/features/admin/components/index.js +1 -0
  131. package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
  132. package/dist/features/admin/repository/chat.repository.js +8 -8
  133. package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
  134. package/dist/features/admin/repository/site-settings.repository.js +11 -9
  135. package/dist/features/admin/schemas/firestore.d.ts +38 -0
  136. package/dist/features/admin/schemas/firestore.js +15 -0
  137. package/dist/features/auctions/components/AuctionBidsTable.d.ts +13 -0
  138. package/dist/features/auctions/components/AuctionBidsTable.js +62 -0
  139. package/dist/features/auctions/components/AuctionDetailPageView.js +15 -21
  140. package/dist/features/auctions/components/AuctionFilters.d.ts +7 -1
  141. package/dist/features/auctions/components/AuctionFilters.js +3 -2
  142. package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
  143. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
  144. package/dist/features/auctions/components/index.d.ts +2 -1
  145. package/dist/features/auctions/components/index.js +1 -1
  146. package/dist/features/auctions/schemas/index.d.ts +14 -14
  147. package/dist/features/auth/actions/realtime-token-actions.js +19 -4
  148. package/dist/features/auth/components/RegisterForm.js +8 -4
  149. package/dist/features/auth/consent-otp.d.ts +2 -0
  150. package/dist/features/auth/consent-otp.js +8 -0
  151. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  152. package/dist/features/auth/schemas/firestore.js +18 -25
  153. package/dist/features/auth/schemas/index.d.ts +2 -2
  154. package/dist/features/before-after/api/[id]/route.js +8 -6
  155. package/dist/features/before-after/schemas/index.d.ts +2 -2
  156. package/dist/features/blog/actions/blog-actions.d.ts +3 -3
  157. package/dist/features/blog/components/BlogIndexListing.js +21 -2
  158. package/dist/features/blog/components/BlogPostForm.js +11 -5
  159. package/dist/features/blog/components/BlogPostView.js +10 -2
  160. package/dist/features/blog/schemas/index.d.ts +18 -18
  161. package/dist/features/cart/components/CartDrawer.js +3 -2
  162. package/dist/features/cart/components/CartView.js +2 -1
  163. package/dist/features/cart/components/CheckoutView.js +2 -1
  164. package/dist/features/cart/repository/cart.repository.js +4 -3
  165. package/dist/features/cart/utils/pending-ops.d.ts +1 -1
  166. package/dist/features/categories/api/[id]/route.js +4 -3
  167. package/dist/features/categories/api/route.js +4 -3
  168. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  169. package/dist/features/categories/components/BrandDetailTabs.d.ts +5 -1
  170. package/dist/features/categories/components/BrandDetailTabs.js +30 -16
  171. package/dist/features/categories/components/BundleCollage.d.ts +8 -0
  172. package/dist/features/categories/components/BundleCollage.js +50 -0
  173. package/dist/features/categories/components/BundleDetailView.js +2 -10
  174. package/dist/features/categories/components/BundleItemsPicker.js +20 -13
  175. package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
  176. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  177. package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -1
  178. package/dist/features/categories/components/CategoryDetailTabs.js +31 -16
  179. package/dist/features/categories/components/CategoryProductsListing.js +52 -13
  180. package/dist/features/categories/components/index.d.ts +2 -0
  181. package/dist/features/categories/components/index.js +1 -0
  182. package/dist/features/categories/schemas/index.d.ts +6 -6
  183. package/dist/features/checkout/actions/checkout-actions.js +4 -3
  184. package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
  185. package/dist/features/classified/components/ClassifiedFilters.d.ts +12 -0
  186. package/dist/features/classified/components/ClassifiedFilters.js +20 -0
  187. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
  188. package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
  189. package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
  190. package/dist/features/classified/components/ClassifiedListView.js +43 -0
  191. package/dist/features/classified/components/index.d.ts +6 -0
  192. package/dist/features/classified/components/index.js +3 -0
  193. package/dist/features/collections/api/[slug]/route.js +16 -12
  194. package/dist/features/collections/schemas/index.d.ts +5 -5
  195. package/dist/features/consultation/api/[id]/route.js +12 -9
  196. package/dist/features/consultation/components/ConsultationForm.js +3 -2
  197. package/dist/features/consultation/schemas/index.d.ts +6 -6
  198. package/dist/features/contact/email.js +77 -116
  199. package/dist/features/copilot/components/AdminCopilotView.js +12 -2
  200. package/dist/features/corporate/api/[id]/route.js +12 -9
  201. package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
  202. package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +12 -0
  203. package/dist/features/digital-codes/components/DigitalCodeFilters.js +17 -0
  204. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
  205. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
  206. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
  207. package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
  208. package/dist/features/digital-codes/components/index.d.ts +6 -0
  209. package/dist/features/digital-codes/components/index.js +3 -0
  210. package/dist/features/events/actions/event-actions.js +4 -3
  211. package/dist/features/events/components/AdminEventEditorView.js +3 -2
  212. package/dist/features/events/components/AdminEventsView.js +4 -2
  213. package/dist/features/events/components/EventPollWidget.js +5 -5
  214. package/dist/features/events/components/EventsIndexListing.js +2 -2
  215. package/dist/features/events/repository/event-entry.repository.d.ts +1 -0
  216. package/dist/features/events/repository/event-entry.repository.js +5 -0
  217. package/dist/features/events/schemas/index.d.ts +20 -20
  218. package/dist/features/faq/actions/faq-actions.d.ts +8 -8
  219. package/dist/features/faq/components/ContactCTA.js +3 -1
  220. package/dist/features/faq/components/FAQAccordion.js +1 -1
  221. package/dist/features/faq/schemas/index.d.ts +4 -4
  222. package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
  223. package/dist/features/filters/AsyncFacetSection.js +65 -0
  224. package/dist/features/filters/index.d.ts +2 -0
  225. package/dist/features/filters/index.js +1 -0
  226. package/dist/features/forms/Checkbox.js +2 -2
  227. package/dist/features/forms/Input.js +2 -2
  228. package/dist/features/forms/Radio.js +5 -5
  229. package/dist/features/forms/Select.js +2 -2
  230. package/dist/features/forms/Slider.js +2 -2
  231. package/dist/features/forms/Textarea.js +2 -2
  232. package/dist/features/homepage/api/[id]/route.js +4 -3
  233. package/dist/features/homepage/api/sections/[id]/route.js +12 -9
  234. package/dist/features/homepage/components/AdSlot.js +2 -1
  235. package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
  236. package/dist/features/homepage/components/BrandsSection.js +2 -2
  237. package/dist/features/homepage/components/CTABannerSection.js +2 -2
  238. package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
  239. package/dist/features/homepage/components/CharacterHotspot.js +119 -157
  240. package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
  241. package/dist/features/homepage/components/CustomCardsSection.js +8 -6
  242. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  243. package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
  244. package/dist/features/homepage/components/HeroBanner.js +5 -4
  245. package/dist/features/homepage/components/HeroCarousel.js +13 -26
  246. package/dist/features/homepage/components/HowItWorksSection.js +4 -2
  247. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  248. package/dist/features/homepage/components/SocialPostCard.js +3 -2
  249. package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
  250. package/dist/features/layout/AppLayoutShell.d.ts +8 -2
  251. package/dist/features/layout/AppLayoutShell.js +54 -78
  252. package/dist/features/layout/BottomActions.js +43 -48
  253. package/dist/features/live/components/LiveItemFilters.d.ts +12 -0
  254. package/dist/features/live/components/LiveItemFilters.js +24 -0
  255. package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
  256. package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
  257. package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
  258. package/dist/features/live/components/LiveItemsListView.js +43 -0
  259. package/dist/features/live/components/index.d.ts +6 -0
  260. package/dist/features/live/components/index.js +3 -0
  261. package/dist/features/media/MediaLightbox.js +8 -14
  262. package/dist/features/media/types/index.d.ts +2 -2
  263. package/dist/features/media/upload/CameraCapture.js +2 -2
  264. package/dist/features/media/upload/ImageUpload.js +4 -2
  265. package/dist/features/media/upload/MediaUploadField.js +24 -8
  266. package/dist/features/media/upload/MediaUploadList.js +1 -1
  267. package/dist/features/orders/actions/order-actions.js +4 -3
  268. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
  269. package/dist/features/orders/components/OrderFilters.js +12 -9
  270. package/dist/features/orders/schemas/index.d.ts +6 -6
  271. package/dist/features/pre-orders/api/[id]/route.js +12 -9
  272. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
  273. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +29 -22
  274. package/dist/features/pre-orders/components/PreOrderFilters.d.ts +7 -1
  275. package/dist/features/pre-orders/components/PreOrderFilters.js +3 -2
  276. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
  277. package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
  278. package/dist/features/pre-orders/components/PreorderCard.js +2 -13
  279. package/dist/features/pre-orders/components/index.d.ts +1 -1
  280. package/dist/features/pre-orders/components/index.js +1 -1
  281. package/dist/features/pre-orders/schemas/index.d.ts +10 -10
  282. package/dist/features/products/api/[id]/route.js +9 -7
  283. package/dist/features/products/api/route.js +1 -1
  284. package/dist/features/products/columns/productTableColumns.js +3 -3
  285. package/dist/features/products/components/AuctionsIndexListing.js +15 -51
  286. package/dist/features/products/components/BidHistory.js +2 -2
  287. package/dist/features/products/components/CompareOverlay.d.ts +2 -0
  288. package/dist/features/products/components/CompareOverlay.js +3 -2
  289. package/dist/features/products/components/MakeOfferButton.js +20 -14
  290. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
  291. package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
  292. package/dist/features/products/components/PrizeDrawCollage.js +54 -14
  293. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  294. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  295. package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
  296. package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
  297. package/dist/features/products/components/PrizeRevealModal.js +24 -14
  298. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  299. package/dist/features/products/components/ProductFilters.d.ts +7 -1
  300. package/dist/features/products/components/ProductFilters.js +11 -8
  301. package/dist/features/products/components/ProductForm.js +18 -16
  302. package/dist/features/products/components/ProductGalleryClient.js +2 -1
  303. package/dist/features/products/components/ProductGrid.js +12 -15
  304. package/dist/features/products/components/ProductsIndexListing.js +16 -52
  305. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  306. package/dist/features/products/repository/products.repository.d.ts +4 -0
  307. package/dist/features/products/repository/products.repository.js +22 -3
  308. package/dist/features/products/schemas/firestore.d.ts +7 -1
  309. package/dist/features/products/schemas/index.d.ts +56 -56
  310. package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
  311. package/dist/features/products/types/index.d.ts +4 -0
  312. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  313. package/dist/features/promotions/schemas/index.d.ts +6 -6
  314. package/dist/features/reviews/actions/review-actions.js +6 -5
  315. package/dist/features/reviews/api/[id]/route.js +4 -3
  316. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  317. package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
  318. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  319. package/dist/features/reviews/schemas/index.d.ts +6 -6
  320. package/dist/features/scams/components/ScamProfileView.js +19 -10
  321. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  322. package/dist/features/search/components/Search.d.ts +1 -1
  323. package/dist/features/search/components/Search.js +56 -49
  324. package/dist/features/search/schemas/index.d.ts +5 -5
  325. package/dist/features/seller/actions/offer-actions.js +11 -9
  326. package/dist/features/seller/components/SellerAddressesView.js +6 -4
  327. package/dist/features/seller/components/SellerBidsView.js +2 -2
  328. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  329. package/dist/features/seller/components/SellerOffersPanel.js +6 -1
  330. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  331. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
  332. package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
  333. package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
  334. package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
  335. package/dist/features/seller/components/SellerProductShell.js +75 -9
  336. package/dist/features/seller/components/SellerProductsView.js +10 -11
  337. package/dist/features/seller/components/SellerShippingView.js +3 -3
  338. package/dist/features/seller/components/SellerSidebar.js +10 -7
  339. package/dist/features/seller/components/SellerStorefrontView.js +3 -3
  340. package/dist/features/seller/schemas/index.d.ts +4 -4
  341. package/dist/features/shell/FormShell.js +2 -1
  342. package/dist/features/shell/QuickFormDrawer.js +4 -3
  343. package/dist/features/shell/StepForm.js +4 -3
  344. package/dist/features/stores/actions/store-query-actions.js +4 -3
  345. package/dist/features/stores/components/StoreAboutView.js +2 -1
  346. package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
  347. package/dist/features/stores/components/StoreDetailLayoutView.js +18 -4
  348. package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
  349. package/dist/features/stores/components/StoreProductsListing.js +16 -49
  350. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  351. package/dist/features/stores/schemas/index.d.ts +2 -2
  352. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
  353. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
  354. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
  355. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
  356. package/dist/index.d.ts +27 -3
  357. package/dist/index.js +1743 -1725
  358. package/dist/monitoring/server-logger.js +5 -9
  359. package/dist/next/api/apiHandler.js +56 -37
  360. package/dist/next/components/GlobalError.js +2 -1
  361. package/dist/next/routing/route-map.d.ts +41 -0
  362. package/dist/next/routing/route-map.js +17 -0
  363. package/dist/react/ErrorBoundary.js +2 -1
  364. package/dist/react/contexts/SessionContext.js +38 -28
  365. package/dist/react/hooks/useCamera.d.ts +1 -1
  366. package/dist/react/hooks/useCamera.js +8 -7
  367. package/dist/seed/products-auctions-seed-data.js +40 -40
  368. package/dist/seed/products-preorders-seed-data.js +16 -16
  369. package/dist/seed/products-prize-draws-seed-data.js +4 -4
  370. package/dist/seed/products-standard-seed-data.js +202 -202
  371. package/dist/seed/site-settings-seed-data.js +6 -0
  372. package/dist/server-entry.d.ts +3 -0
  373. package/dist/server-entry.js +3 -0
  374. package/dist/server.d.ts +12 -0
  375. package/dist/server.js +15 -0
  376. package/dist/tailwind-utilities.css +1 -1
  377. package/dist/ui/DataTable.js +28 -7
  378. package/dist/ui/components/Button.d.ts +8 -1
  379. package/dist/ui/components/Button.js +47 -8
  380. package/dist/ui/components/CollageLightbox.d.ts +18 -0
  381. package/dist/ui/components/CollageLightbox.js +41 -0
  382. package/dist/ui/components/Dropdown.js +7 -6
  383. package/dist/ui/components/ImageLightbox.d.ts +7 -1
  384. package/dist/ui/components/ImageLightbox.js +15 -5
  385. package/dist/ui/components/PageLoader.js +4 -3
  386. package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
  387. package/dist/ui/components/PaginatedMultiSelect.js +141 -0
  388. package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
  389. package/dist/ui/components/Semantic.js +1 -1
  390. package/dist/ui/components/SideDrawer.js +17 -15
  391. package/dist/ui/components/index.style.css +1 -0
  392. package/dist/ui/index.d.ts +2 -0
  393. package/dist/ui/index.js +1 -0
  394. package/dist/utils/number.formatter.js +5 -13
  395. package/dist/utils/string.formatter.js +21 -18
  396. package/dist/validation/schemas.d.ts +2 -2
  397. package/package.json +1 -1
@@ -44,11 +44,11 @@ export declare const couponItemSchema: z.ZodObject<{
44
44
  updatedAt: string;
45
45
  code: string;
46
46
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
47
- scope: "admin" | "seller";
47
+ scope: "seller" | "admin";
48
+ description: string;
48
49
  name: string;
49
50
  isActive: boolean;
50
51
  isPublic: boolean;
51
- description: string;
52
52
  usageCount: number;
53
53
  discountValue: number;
54
54
  storeId?: string | undefined;
@@ -68,11 +68,11 @@ export declare const couponItemSchema: z.ZodObject<{
68
68
  updatedAt: string;
69
69
  code: string;
70
70
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
71
- scope: "admin" | "seller";
71
+ scope: "seller" | "admin";
72
+ description: string;
72
73
  name: string;
73
74
  isActive: boolean;
74
75
  isPublic: boolean;
75
- description: string;
76
76
  usageCount: number;
77
77
  discountValue: number;
78
78
  storeId?: string | undefined;
@@ -99,7 +99,7 @@ export declare const promotionsListParamsSchema: z.ZodObject<{
99
99
  sort?: string | undefined;
100
100
  pageSize?: number | undefined;
101
101
  storeId?: string | undefined;
102
- scope?: "admin" | "seller" | undefined;
102
+ scope?: "seller" | "admin" | undefined;
103
103
  page?: number | undefined;
104
104
  isActive?: boolean | undefined;
105
105
  filters?: string | undefined;
@@ -107,7 +107,7 @@ export declare const promotionsListParamsSchema: z.ZodObject<{
107
107
  sort?: string | undefined;
108
108
  pageSize?: number | undefined;
109
109
  storeId?: string | undefined;
110
- scope?: "admin" | "seller" | undefined;
110
+ scope?: "seller" | "admin" | undefined;
111
111
  page?: number | undefined;
112
112
  isActive?: boolean | undefined;
113
113
  filters?: string | undefined;
@@ -12,6 +12,7 @@ import { reviewRepository } from "../repository/reviews.repository";
12
12
  import { productRepository } from "../../products/repository/products.repository";
13
13
  import { ProductStatusValues } from "../../products/schemas";
14
14
  import { userRepository } from "../../auth/repository/user.repository";
15
+ const ERR_REVIEW_ID_REQUIRED = "Review ID is required";
15
16
  export async function createReview(userId, input) {
16
17
  const product = await productRepository.findById(input.productId);
17
18
  if (!product) {
@@ -51,7 +52,7 @@ export async function createReview(userId, input) {
51
52
  }
52
53
  export async function updateReview(userId, reviewId, input) {
53
54
  if (!reviewId?.trim()) {
54
- throw new ValidationError("reviewId is required");
55
+ throw new ValidationError(ERR_REVIEW_ID_REQUIRED);
55
56
  }
56
57
  const existing = await reviewRepository.findById(reviewId);
57
58
  if (!existing) {
@@ -78,7 +79,7 @@ export async function updateReview(userId, reviewId, input) {
78
79
  }
79
80
  export async function deleteReview(userId, reviewId) {
80
81
  if (!reviewId?.trim()) {
81
- throw new ValidationError("reviewId is required");
82
+ throw new ValidationError(ERR_REVIEW_ID_REQUIRED);
82
83
  }
83
84
  const existing = await reviewRepository.findById(reviewId);
84
85
  if (!existing) {
@@ -91,7 +92,7 @@ export async function deleteReview(userId, reviewId) {
91
92
  }
92
93
  export async function adminUpdateReview(adminId, reviewId, input) {
93
94
  if (!reviewId?.trim()) {
94
- throw new ValidationError("reviewId is required");
95
+ throw new ValidationError(ERR_REVIEW_ID_REQUIRED);
95
96
  }
96
97
  const existing = await reviewRepository.findById(reviewId);
97
98
  if (!existing) {
@@ -119,7 +120,7 @@ export async function adminUpdateReview(adminId, reviewId, input) {
119
120
  }
120
121
  export async function adminDeleteReview(adminId, reviewId) {
121
122
  if (!reviewId?.trim()) {
122
- throw new ValidationError("reviewId is required");
123
+ throw new ValidationError(ERR_REVIEW_ID_REQUIRED);
123
124
  }
124
125
  const existing = await reviewRepository.findById(reviewId);
125
126
  if (!existing) {
@@ -133,7 +134,7 @@ export async function adminDeleteReview(adminId, reviewId) {
133
134
  }
134
135
  export async function voteReviewHelpful(reviewId, helpful) {
135
136
  if (!reviewId?.trim()) {
136
- throw new ValidationError("reviewId is required");
137
+ throw new ValidationError(ERR_REVIEW_ID_REQUIRED);
137
138
  }
138
139
  if (helpful) {
139
140
  await reviewRepository.incrementHelpful(reviewId);
@@ -12,6 +12,7 @@ import { NextResponse } from "next/server.js";
12
12
  import { z } from "zod";
13
13
  import { getProviders } from "../../../../contracts";
14
14
  import { createRouteHandler } from "../../../../next";
15
+ const ERR_REVIEW_NOT_FOUND = "Review not found";
15
16
  // ---- GET /api/reviews/[id] ---------------------------------------------------
16
17
  export async function GET(_request, context) {
17
18
  try {
@@ -23,7 +24,7 @@ export async function GET(_request, context) {
23
24
  const repo = db.getRepository("reviews");
24
25
  const review = await repo.findById(id);
25
26
  if (!review) {
26
- return NextResponse.json({ success: false, error: "Review not found" }, { status: 404 });
27
+ return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
27
28
  }
28
29
  return NextResponse.json({ success: true, data: review });
29
30
  }
@@ -65,7 +66,7 @@ export const reviewItemPATCH = createRouteHandler({
65
66
  const repo = db.getRepository("reviews");
66
67
  const review = await repo.findById(id);
67
68
  if (!review)
68
- return NextResponse.json({ success: false, error: "Review not found" }, { status: 404 });
69
+ return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
69
70
  const isOwner = review.userId === user.uid;
70
71
  const isModerator = user.role === "moderator";
71
72
  const isAdmin = user.role === "admin";
@@ -96,7 +97,7 @@ export const reviewItemDELETE = createRouteHandler({
96
97
  const repo = db.getRepository("reviews");
97
98
  const review = await repo.findById(id);
98
99
  if (!review)
99
- return NextResponse.json({ success: false, error: "Review not found" }, { status: 404 });
100
+ return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
100
101
  const isOwner = review.userId === user.uid;
101
102
  const isModerator = user.role === "moderator";
102
103
  const isAdmin = user.role === "admin";
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
3
  import { reviewRepository } from "../../../repositories";
4
- import { Container, Main, Section } from "../../../ui";
4
+ import { Container, Heading, Main, Section, Text } from "../../../ui";
5
5
  import { ROUTES } from "../../../next";
6
6
  import { ReviewDetailShell } from "./ReviewDetailShell";
7
7
  export async function ReviewDetailPageView({ id }) {
@@ -10,7 +10,7 @@ export async function ReviewDetailPageView({ id }) {
10
10
  // storeId === storeSlug for stores in this project (pure slug IDs)
11
11
  const storeSlug = doc?.storeId ?? null;
12
12
  if (!review || review.status !== "approved") {
13
- return (_jsx(Main, { children: _jsx(Section, { className: "py-24", children: _jsx(Container, { size: "sm", children: _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-5xl mb-4", "aria-hidden": "true", children: "\uD83D\uDD0D" }), _jsx("h1", { className: "text-2xl font-bold text-neutral-900 dark:text-white mb-2", children: "Review not found" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-zinc-400 mb-6", children: "This review may have been removed or is no longer available." }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-600 transition-colors", children: "\u2190 Back to Reviews" })] }) }) }) }));
13
+ return (_jsx(Main, { children: _jsx(Section, { className: "py-24", children: _jsx(Container, { size: "sm", children: _jsxs("div", { className: "text-center", children: [_jsx(Text, { className: "text-5xl mb-4", "aria-hidden": "true", children: "\uD83D\uDD0D" }), _jsx(Heading, { level: 1, className: "text-2xl font-bold text-neutral-900 dark:text-white mb-2", children: "Review not found" }), _jsx(Text, { className: "text-sm text-neutral-500 dark:text-zinc-400 mb-6", children: "This review may have been removed or is no longer available." }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-600 transition-colors", children: "\u2190 Back to Reviews" })] }) }) }) }));
14
14
  }
15
15
  return (_jsxs(Main, { children: [_jsx("div", { className: "border-b border-neutral-100 dark:border-zinc-800 bg-neutral-50 dark:bg-zinc-950 py-2.5 px-4", children: _jsxs("nav", { className: "mx-auto max-w-3xl flex items-center gap-1.5 text-xs text-neutral-400 dark:text-zinc-500", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary transition-colors", children: "Home" }), _jsx("span", { children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "hover:text-primary transition-colors", children: "Reviews" }), _jsx("span", { children: "/" }), _jsx("span", { className: "text-neutral-700 dark:text-zinc-300 truncate max-w-[200px]", children: review.title ?? review.id })] }) }), _jsx(ReviewDetailShell, { review: review, storeHref: storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null })] }));
16
16
  }
@@ -1,8 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useCallback } from "react";
4
+ const CLS_RELATED_LINK = "group flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 hover:border-primary hover:shadow-sm transition-all";
5
+ const CLS_RELATED_LABEL = "text-xs text-neutral-400 dark:text-zinc-500 mb-0.5";
6
+ const CLS_RELATED_TITLE = "text-sm font-medium text-neutral-900 dark:text-white truncate group-hover:text-primary transition-colors";
4
7
  import Link from "next/link";
5
- import { RichText, Span, StarRating } from "../../../ui";
8
+ import { Heading, RichText, Section, Span, StarRating, Text } from "../../../ui";
6
9
  import { maskName } from "../../../security";
7
10
  import { getDefaultLocale } from "../../../core/baseline-resolver";
8
11
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
@@ -73,9 +76,9 @@ export function ReviewDetailShell({ review, storeHref }) {
73
76
  ? String(ROUTES.PUBLIC.PROFILE(review.userId))
74
77
  : null;
75
78
  const currentImage = lightboxIdx !== null ? images[lightboxIdx] : null;
76
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-neutral-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 pb-8 pt-10", children: _jsxs("div", { className: "mx-auto max-w-3xl px-4", children: [_jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs("span", { className: "text-2xl font-bold text-neutral-900 dark:text-white", children: [review.rating, ".0"] }), review.verified && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "\u2713 Verified Purchase" })), review.featured && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-yellow-100 px-3 py-1 text-xs font-semibold text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", children: "\u2605 Featured" }))] }), review.title && (_jsx("h1", { className: "text-2xl font-bold text-neutral-900 dark:text-white mb-4 leading-snug", children: review.title })), _jsxs("div", { className: "flex items-center gap-3", children: [review.userAvatar ? (_jsx("div", { role: "img", "aria-label": displayName, className: "h-11 w-11 flex-shrink-0 rounded-full bg-center bg-cover ring-2 ring-white dark:ring-zinc-800", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx("div", { className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-base font-bold text-primary ring-2 ring-white dark:ring-zinc-800", children: initials })), _jsxs("div", { className: "min-w-0", children: [reviewerHref ? (_jsx(Link, { href: reviewerHref, className: "text-sm font-semibold text-neutral-900 dark:text-white hover:text-primary transition-colors", children: displayName })) : (_jsx("span", { className: "text-sm font-semibold text-neutral-900 dark:text-white", children: review.isAnonymous ? "Anonymous" : displayName })), date && (_jsx("p", { className: "text-xs text-neutral-400 dark:text-zinc-500 mt-0.5", children: date }))] })] })] }) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-8 space-y-8", children: [review.comment && (_jsx("section", { children: _jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-neutral dark:prose-invert max-w-none prose-p:leading-relaxed prose-headings:font-semibold prose-img:rounded-lg prose-a:text-primary", className: "text-neutral-700 dark:text-zinc-300" }) })), images.length > 0 && (_jsxs("section", { children: [_jsxs("h2", { className: "text-sm font-semibold uppercase tracking-wide text-neutral-400 dark:text-zinc-500 mb-3", children: ["Photos (", images.length, ")"] }), _jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2", children: images.map((img, i) => (_jsxs("button", { type: "button", onClick: () => setLightboxIdx(i), "aria-label": `View photo ${i + 1}`, className: "group relative aspect-square overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-neutral-100 dark:bg-zinc-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsx("div", { className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30", children: _jsx("span", { className: "text-white text-xl", children: "\uD83D\uDD0D" }) })] }, i))) })] })), review.video && (_jsxs("section", { children: [_jsx("h2", { className: "text-sm font-semibold uppercase tracking-wide text-neutral-400 dark:text-zinc-500 mb-3", children: "Video" }), _jsx("div", { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-black aspect-video", children: _jsx("video", { src: review.video.url, poster: review.video.thumbnailUrl, controls: true, className: "h-full w-full", preload: "metadata" }) })] })), _jsxs("section", { className: "flex items-center gap-4 py-4 border-t border-neutral-100 dark:border-zinc-800", children: [_jsx("div", { className: "text-sm text-neutral-500 dark:text-zinc-400", children: helpfulCount > 0 && (_jsxs("span", { children: [_jsx("strong", { className: "text-neutral-900 dark:text-white", children: helpfulCount }), " ", helpfulCount === 1 ? "person" : "people", " found this helpful"] })) }), _jsxs("button", { type: "button", onClick: handleVote, disabled: voted || voting, className: `ml-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors ${voted
79
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-neutral-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 pb-8 pt-10", children: _jsxs("div", { className: "mx-auto max-w-3xl px-4", children: [_jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs("span", { className: "text-2xl font-bold text-neutral-900 dark:text-white", children: [review.rating, ".0"] }), review.verified && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", children: "\u2713 Verified Purchase" })), review.featured && (_jsx("span", { className: "inline-flex items-center gap-1 rounded-full bg-yellow-100 px-3 py-1 text-xs font-semibold text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", children: "\u2605 Featured" }))] }), review.title && (_jsx(Heading, { level: 1, className: "text-2xl font-bold text-neutral-900 dark:text-white mb-4 leading-snug", children: review.title })), _jsxs("div", { className: "flex items-center gap-3", children: [review.userAvatar ? (_jsx("div", { role: "img", "aria-label": displayName, className: "h-11 w-11 flex-shrink-0 rounded-full bg-center bg-cover ring-2 ring-white dark:ring-zinc-800", style: { backgroundImage: `url(${review.userAvatar})` } })) : (_jsx("div", { className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-base font-bold text-primary ring-2 ring-white dark:ring-zinc-800", children: initials })), _jsxs("div", { className: "min-w-0", children: [reviewerHref ? (_jsx(Link, { href: reviewerHref, className: "text-sm font-semibold text-neutral-900 dark:text-white hover:text-primary transition-colors", children: displayName })) : (_jsx("span", { className: "text-sm font-semibold text-neutral-900 dark:text-white", children: review.isAnonymous ? "Anonymous" : displayName })), date && (_jsx(Text, { className: "text-xs text-neutral-400 dark:text-zinc-500 mt-0.5", children: date }))] })] })] }) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-8 space-y-8", children: [review.comment && (_jsx(Section, { children: _jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-neutral dark:prose-invert max-w-none prose-p:leading-relaxed prose-headings:font-semibold prose-img:rounded-lg prose-a:text-primary", className: "text-neutral-700 dark:text-zinc-300" }) })), images.length > 0 && (_jsxs(Section, { children: [_jsxs(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-neutral-400 dark:text-zinc-500 mb-3", children: ["Photos (", images.length, ")"] }), _jsx("div", { className: "grid grid-cols-3 sm:grid-cols-4 gap-2", children: images.map((img, i) => (_jsxs("button", { type: "button", onClick: () => setLightboxIdx(i), "aria-label": `View photo ${i + 1}`, className: "group relative aspect-square overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-neutral-100 dark:bg-zinc-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsx("div", { className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30", children: _jsx("span", { className: "text-white text-xl", children: "\uD83D\uDD0D" }) })] }, i))) })] })), review.video && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "text-sm font-semibold uppercase tracking-wide text-neutral-400 dark:text-zinc-500 mb-3", children: "Video" }), _jsx("div", { className: "overflow-hidden rounded-xl border border-neutral-200 dark:border-zinc-700 bg-black aspect-video", children: _jsx("video", { src: review.video.url, poster: review.video.thumbnailUrl, controls: true, className: "h-full w-full", preload: "metadata" }) })] })), _jsxs(Section, { className: "flex items-center gap-4 py-4 border-t border-neutral-100 dark:border-zinc-800", children: [_jsx("div", { className: "text-sm text-neutral-500 dark:text-zinc-400", children: helpfulCount > 0 && (_jsxs("span", { children: [_jsx("strong", { className: "text-neutral-900 dark:text-white", children: helpfulCount }), " ", helpfulCount === 1 ? "person" : "people", " found this helpful"] })) }), _jsxs("button", { type: "button", onClick: handleVote, disabled: voted || voting, className: `ml-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors ${voted
77
80
  ? "border-emerald-200 bg-emerald-50 text-emerald-600 dark:border-emerald-800 dark:bg-emerald-900/20 dark:text-emerald-400 cursor-default"
78
- : "border-neutral-300 dark:border-zinc-600 text-neutral-700 dark:text-zinc-200 hover:border-primary hover:text-primary dark:hover:border-primary dark:hover:text-primary disabled:opacity-50"}`, children: [_jsx("span", { "aria-hidden": "true", children: voted ? "✓" : "👍" }), voted ? "Marked helpful" : voting ? "Saving…" : "Helpful?"] })] }), _jsxs("section", { className: "grid gap-3 sm:grid-cols-3", children: [productHref && (_jsxs(Link, { href: productHref, className: "group flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 hover:border-primary hover:shadow-sm transition-all", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-orange-100 dark:bg-orange-900/30 text-xl", children: "\uD83D\uDCE6" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs text-neutral-400 dark:text-zinc-500 mb-0.5", children: "Product" }), _jsx("p", { className: "text-sm font-medium text-neutral-900 dark:text-white truncate group-hover:text-primary transition-colors", children: review.productTitle ?? "View Product" })] })] })), sellerHref && (_jsxs(Link, { href: sellerHref, className: "group flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 hover:border-primary hover:shadow-sm transition-all", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100 dark:bg-blue-900/30 text-xl", children: "\uD83C\uDFEA" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs text-neutral-400 dark:text-zinc-500 mb-0.5", children: "Seller" }), _jsx("p", { className: "text-sm font-medium text-neutral-900 dark:text-white truncate group-hover:text-primary transition-colors", children: "View Seller" })] })] })), reviewerHref ? (_jsxs(Link, { href: reviewerHref, className: "group flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4 hover:border-primary hover:shadow-sm transition-all", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs text-neutral-400 dark:text-zinc-500 mb-0.5", children: "Reviewer" }), _jsx("p", { className: "text-sm font-medium text-neutral-900 dark:text-white truncate group-hover:text-primary transition-colors", children: displayName })] })] })) : (_jsxs("div", { className: "flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Span, { className: "block text-xs text-neutral-400 dark:text-zinc-500 mb-0.5", children: "Reviewer" }), _jsx(Span, { className: "block text-sm font-medium text-neutral-900 dark:text-white truncate", children: "Anonymous" })] })] }))] })] }), lightboxIdx !== null && currentImage && (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/95", onClick: closeLightbox, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: [_jsx("button", { type: "button", onClick: closeLightbox, "aria-label": "Close lightbox", className: "absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/20 transition-colors text-xl", children: "\u00D7" }), _jsxs("div", { className: "absolute top-4 left-1/2 -translate-x-1/2 text-white/70 text-sm", children: [lightboxIdx + 1, " / ", images.length] }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); prevImage(); }, "aria-label": "Previous image", className: "absolute left-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u2039" })), _jsx("div", { className: "max-h-[85vh] max-w-[85vw] flex items-center justify-center", onClick: (e) => e.stopPropagation(), children: _jsx("img", { src: currentImage.url, alt: `Review photo ${lightboxIdx + 1}`, className: "max-h-[85vh] max-w-[85vw] rounded-lg object-contain shadow-2xl" }) }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); nextImage(); }, "aria-label": "Next image", className: "absolute right-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u203A" })), images.length > 1 && (_jsx("div", { className: "absolute bottom-4 left-0 right-0 flex justify-center gap-2 px-4", children: images.map((img, i) => (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); setLightboxIdx(i); }, "aria-label": `Go to image ${i + 1}`, className: `h-12 w-12 flex-shrink-0 rounded-md bg-center bg-cover border-2 transition-all ${i === lightboxIdx
81
+ : "border-neutral-300 dark:border-zinc-600 text-neutral-700 dark:text-zinc-200 hover:border-primary hover:text-primary dark:hover:border-primary dark:hover:text-primary disabled:opacity-50"}`, children: [_jsx("span", { "aria-hidden": "true", children: voted ? "✓" : "👍" }), voted ? "Marked helpful" : voting ? "Saving…" : "Helpful?"] })] }), _jsxs(Section, { className: "grid gap-3 sm:grid-cols-3", children: [productHref && (_jsxs(Link, { href: productHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-orange-100 dark:bg-orange-900/30 text-xl", children: "\uD83D\uDCE6" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Product" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: review.productTitle ?? "View Product" })] })] })), sellerHref && (_jsxs(Link, { href: sellerHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100 dark:bg-blue-900/30 text-xl", children: "\uD83C\uDFEA" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Seller" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: "View Seller" })] })] })), reviewerHref ? (_jsxs(Link, { href: reviewerHref, className: CLS_RELATED_LINK, children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Reviewer" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: displayName })] })] })) : (_jsxs("div", { className: "flex items-center gap-3 rounded-xl border border-neutral-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-4", children: [_jsx("span", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30 text-xl", children: "\uD83D\uDC64" }), _jsxs("div", { className: "min-w-0", children: [_jsx(Span, { className: "block text-xs text-neutral-400 dark:text-zinc-500 mb-0.5", children: "Reviewer" }), _jsx(Span, { className: "block text-sm font-medium text-neutral-900 dark:text-white truncate", children: "Anonymous" })] })] }))] })] }), lightboxIdx !== null && currentImage && (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/95", onClick: closeLightbox, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: [_jsx("button", { type: "button", onClick: closeLightbox, "aria-label": "Close lightbox", className: "absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/20 transition-colors text-xl", children: "\u00D7" }), _jsxs("div", { className: "absolute top-4 left-1/2 -translate-x-1/2 text-white/70 text-sm", children: [lightboxIdx + 1, " / ", images.length] }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); prevImage(); }, "aria-label": "Previous image", className: "absolute left-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u2039" })), _jsx("div", { className: "max-h-[85vh] max-w-[85vw] flex items-center justify-center", onClick: (e) => e.stopPropagation(), children: _jsx("img", { src: currentImage.url, alt: `Review photo ${lightboxIdx + 1}`, className: "max-h-[85vh] max-w-[85vw] rounded-lg object-contain shadow-2xl" }) }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); nextImage(); }, "aria-label": "Next image", className: "absolute right-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u203A" })), images.length > 1 && (_jsx("div", { className: "absolute bottom-4 left-0 right-0 flex justify-center gap-2 px-4", children: images.map((img, i) => (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); setLightboxIdx(i); }, "aria-label": `Go to image ${i + 1}`, className: `h-12 w-12 flex-shrink-0 rounded-md bg-center bg-cover border-2 transition-all ${i === lightboxIdx
79
82
  ? "border-white scale-110"
80
83
  : "border-transparent opacity-60 hover:opacity-100"}`, style: { backgroundImage: `url(${img.thumbnailUrl ?? img.url})` } }, i))) }))] }))] }));
81
84
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback, useMemo } from "react";
4
4
  import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
- import { Pagination, ListingToolbar } from "../../../ui";
6
+ import { ListingToolbar, Pagination, Text } from "../../../ui";
7
7
  import { ReviewCard } from "./ReviewsList";
8
8
  import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
9
9
  import { useReviews } from "../hooks/useReviews";
@@ -96,5 +96,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
96
96
  value: opt.value,
97
97
  label: opt.key,
98
98
  }));
99
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
99
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: _jsxs("div", { className: "p-4 space-y-3", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No reviews found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
100
100
  }
@@ -108,13 +108,13 @@ export declare const reviewSchema: z.ZodObject<{
108
108
  thumbnailUrl?: string | undefined;
109
109
  duration?: number | undefined;
110
110
  } | undefined;
111
- productTitle?: string | undefined;
112
111
  featured?: boolean | undefined;
113
- storeName?: string | undefined;
114
112
  images?: {
115
113
  url: string;
116
114
  thumbnailUrl?: string | undefined;
117
115
  }[] | undefined;
116
+ storeName?: string | undefined;
117
+ productTitle?: string | undefined;
118
118
  storeSlug?: string | undefined;
119
119
  userAvatar?: string | undefined;
120
120
  comment?: string | undefined;
@@ -138,13 +138,13 @@ export declare const reviewSchema: z.ZodObject<{
138
138
  thumbnailUrl?: string | undefined;
139
139
  duration?: number | undefined;
140
140
  } | undefined;
141
- productTitle?: string | undefined;
142
141
  featured?: boolean | undefined;
143
- storeName?: string | undefined;
144
142
  images?: {
145
143
  url: string;
146
144
  thumbnailUrl?: string | undefined;
147
145
  }[] | undefined;
146
+ storeName?: string | undefined;
147
+ productTitle?: string | undefined;
148
148
  storeSlug?: string | undefined;
149
149
  userAvatar?: string | undefined;
150
150
  comment?: string | undefined;
@@ -168,9 +168,9 @@ export declare const reviewListParamsSchema: z.ZodObject<{
168
168
  perPage?: number | undefined;
169
169
  storeId?: string | undefined;
170
170
  status?: "pending" | "approved" | "rejected" | undefined;
171
+ featured?: boolean | undefined;
171
172
  page?: number | undefined;
172
173
  productId?: string | undefined;
173
- featured?: boolean | undefined;
174
174
  rating?: number | undefined;
175
175
  userId?: string | undefined;
176
176
  }, {
@@ -178,9 +178,9 @@ export declare const reviewListParamsSchema: z.ZodObject<{
178
178
  perPage?: number | undefined;
179
179
  storeId?: string | undefined;
180
180
  status?: "pending" | "approved" | "rejected" | undefined;
181
+ featured?: boolean | undefined;
181
182
  page?: number | undefined;
182
183
  productId?: string | undefined;
183
- featured?: boolean | undefined;
184
184
  rating?: number | undefined;
185
185
  userId?: string | undefined;
186
186
  }>;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const CLS_OUTLINE_BTN = "appkit-button appkit-button--outline appkit-button--md flex w-full items-center gap-2";
2
3
  import Link from "next/link";
3
4
  import { Shield, Phone, Wallet, Mail, AlertOctagon, Flag, Eye, Users, Calendar, ExternalLink, FileText, MessageSquare, Link2, } from "lucide-react";
4
5
  import { Container, Heading, Main, Section, Text, Grid, Stack, Row, Card, CardHeader, CardBody, Badge, Alert, Breadcrumb, EmptyState, } from "../../../ui";
@@ -30,22 +31,30 @@ function statusVariant(status) {
30
31
  return "warning";
31
32
  return "inactive";
32
33
  }
34
+ // ─── Sub-components ───────────────────────────────────────────────────────────
35
+ function ScammerHeaderCard({ scammer }) {
36
+ return (_jsx(Card, { variant: "elevated", padding: "lg", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", gap: "md", align: "start", className: "flex-wrap", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, className: "text-2xl font-bold", children: scammer.displayNames[0] }), scammer.displayNames.length > 1 && (_jsxs(Text, { variant: "secondary", className: "text-sm", children: ["Also known as: ", scammer.displayNames.slice(1).join(", ")] }))] }), _jsx(Badge, { variant: statusVariant(scammer.status), children: SCAMMER_STATUS_LABELS[scammer.status] ?? scammer.status })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform] }), scammer.tags.includes("repeat_offender") && (_jsx(Badge, { variant: "danger", children: "Repeat Offender" }))] }), _jsxs(Row, { gap: "md", wrap: true, children: [_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Eye, { className: "h-4 w-4" }), scammer.views, " views"] }), _jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Users, { className: "h-4 w-4" }), scammer.incidentCount + 1, " victim", scammer.incidentCount !== 0 ? "s" : "", " reported"] }), _jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Calendar, { className: "h-4 w-4" }), "Reported ", formatDate(scammer.createdAt)] }), scammer.amountLost ? (_jsxs(Text, { className: "text-sm font-medium text-[color:var(--appkit-color-danger,theme(colors.red.600))]", children: [formatPaise(scammer.amountLost), " lost (primary incident)"] })) : null] })] }) }));
37
+ }
38
+ function ScammerIncidentsSection({ incidents }) {
39
+ return (_jsxs(Stack, { gap: "sm", children: [_jsx(Row, { justify: "between", align: "center", children: _jsxs(Heading, { level: 2, className: "text-base font-semibold", children: ["Additional Incidents", incidents.length > 0 && ` (${incidents.length})`] }) }), incidents.length === 0 ? (_jsx(EmptyState, { icon: _jsx(FileText, { className: "h-8 w-8" }), title: "No additional incidents yet", description: "Other victims' verified reports linked to this profile will appear here." })) : (_jsx(Stack, { gap: "sm", children: incidents.map((inc) => (_jsx(Card, { variant: "outlined", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", className: "flex-wrap", children: [_jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[inc.scamType] ?? inc.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[inc.scamPlatform] ?? inc.scamPlatform] })] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: formatDate(inc.createdAt) })] }), inc.itemInvolved && (_jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Item: ", inc.itemInvolved] })), inc.amountLost ? (_jsxs(Text, { className: "text-xs font-medium text-[color:var(--appkit-color-danger,theme(colors.red.600))]", children: [formatPaise(inc.amountLost), " lost"] })) : null, _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: inc.description.length > 200
40
+ ? `${inc.description.slice(0, 200).trimEnd()}…`
41
+ : inc.description }), _jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Reported by: ", inc.reportedByAnon ? "Anonymous" : "Verified victim"] })] }) }, inc.id))) }))] }));
42
+ }
43
+ function ScammerCommentsSection({ comments, isAuthenticated, scammerId, }) {
44
+ const commentLoginHref = `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(`/scams/${scammerId}`)}`;
45
+ return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 2, className: "text-base font-semibold", children: ["Community Discussion", comments.length > 0 && ` (${comments.length})`] }), isAuthenticated ? (_jsx(Link, { href: String(ROUTES.PUBLIC.SCAM_REPORT), className: "text-xs text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: "Leave a comment" })) : (_jsx(Link, { href: commentLoginHref, className: "text-xs text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: "Sign in to comment" }))] }), comments.length === 0 ? (_jsx(EmptyState, { icon: _jsx(MessageSquare, { className: "h-8 w-8" }), title: "No comments yet", description: "Community comments and victim testimonials appear here." })) : (_jsx(Stack, { gap: "sm", children: comments.map((c) => (_jsx(Card, { variant: "flat", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "center", gap: "sm", children: [_jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Text, { className: "text-sm font-medium", children: c.authorDisplayName }), c.authorRole !== "user" && (_jsx(Badge, { variant: "default", className: "text-[10px]", children: c.authorRole })), c.isAccused && c.isAccusedVerified && (_jsx(Badge, { variant: "warning", className: "text-[10px]", children: "Accused" })), c.isVerifiedVictim && (_jsx(Badge, { variant: "success", className: "text-[10px]", children: "Verified Victim" }))] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: formatDate(c.createdAt) })] }), _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: c.body })] }) }, c.id))) }))] }));
46
+ }
47
+ function ScammerActionsColumn({ scammer, isAuthenticated, reportHref, contestLoginHref, }) {
48
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Card, { variant: "elevated", padding: "md", children: [_jsx(CardHeader, { children: _jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide", children: "Actions" }) }), _jsx(CardBody, { children: _jsxs(Stack, { gap: "sm", children: [isAuthenticated ? (_jsxs(Link, { href: reportHref, className: CLS_OUTLINE_BTN, children: [_jsx(AlertOctagon, { className: "h-4 w-4 text-[color:var(--appkit-color-danger,theme(colors.red.500))]" }), "Report another incident"] })) : (_jsxs(Link, { href: `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(reportHref)}`, className: CLS_OUTLINE_BTN, children: [_jsx(AlertOctagon, { className: "h-4 w-4 text-[color:var(--appkit-color-danger,theme(colors.red.500))]" }), "Sign in to report an incident"] })), isAuthenticated ? (_jsxs(Link, { href: `/scams/${scammer.id}/contest`, className: CLS_OUTLINE_BTN, children: [_jsx(Flag, { className: "h-4 w-4 text-[color:var(--appkit-color-warning,theme(colors.amber.500))]" }), "Contest this profile"] })) : (_jsxs(Link, { href: contestLoginHref, className: "appkit-button appkit-button--ghost appkit-button--md flex w-full items-center gap-2", children: [_jsx(Flag, { className: "h-4 w-4" }), "Sign in to contest"] })), _jsx(Text, { variant: "secondary", className: "text-xs", children: "All contest submissions are reviewed by our moderation team before any changes are made." })] }) })] }), _jsxs(Card, { variant: "outlined", padding: "md", children: [_jsx(CardHeader, { children: _jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide", children: "Contest Options" }) }), _jsx(CardBody, { children: _jsx(Stack, { gap: "xs", as: "ul", children: Object.entries(CONTEST_TYPE_LABELS).map(([, label]) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx("span", { className: "mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-[color:var(--appkit-color-border,theme(colors.zinc.300))]" }), _jsx(Text, { variant: "secondary", className: "text-xs", children: label })] }, label))) }) })] }), scammer.verifiedAt && (_jsx(Alert, { variant: "success", compact: true, children: _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Shield, { className: "h-3.5 w-3.5 shrink-0" }), _jsxs(Text, { className: "text-xs font-medium", children: ["Verified by LetItRip moderation on ", formatDate(scammer.verifiedAt)] })] }) }))] }));
49
+ }
33
50
  export function ScamProfileView({ scammer, isAuthenticated, incidents = [], comments = [], relatedScammers = [], }) {
34
51
  const reportHref = String(ROUTES.PUBLIC.SCAM_REPORT);
35
52
  const registryHref = String(ROUTES.PUBLIC.SCAMS);
36
53
  const contestLoginHref = `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(`/scams/${scammer.id}`)}`;
54
+ const scamTypeDef = getScamType(scammer.scamType);
37
55
  return (_jsxs(Main, { children: [_jsx("div", { className: "border-b appkit-breadcrumb-strip", children: _jsx(Container, { size: "xl", className: "py-3", children: _jsx(Breadcrumb, { items: [
38
56
  { label: "Home", href: "/" },
39
57
  { label: "Scam Registry", href: registryHref },
40
58
  { label: scammer.displayNames[0] ?? "Profile" },
41
- ] }) }) }), _jsx(Section, { className: "py-10", children: _jsx(Container, { size: "xl", children: _jsxs(Grid, { cols: "twoThird", gap: "lg", children: [_jsxs(Stack, { gap: "lg", children: [_jsx(Card, { variant: "elevated", padding: "lg", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", gap: "md", align: "start", className: "flex-wrap", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, className: "text-2xl font-bold", children: scammer.displayNames[0] }), scammer.displayNames.length > 1 && (_jsxs(Text, { variant: "secondary", className: "text-sm", children: ["Also known as: ", scammer.displayNames.slice(1).join(", ")] }))] }), _jsx(Badge, { variant: statusVariant(scammer.status), children: SCAMMER_STATUS_LABELS[scammer.status] ?? scammer.status })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform] }), scammer.tags.includes("repeat_offender") && (_jsx(Badge, { variant: "danger", children: "Repeat Offender" }))] }), _jsxs(Row, { gap: "md", wrap: true, children: [_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Eye, { className: "h-4 w-4" }), scammer.views, " views"] }), _jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Users, { className: "h-4 w-4" }), scammer.incidentCount + 1, " victim", scammer.incidentCount !== 0 ? "s" : "", " reported"] }), _jsxs(Text, { variant: "secondary", className: "flex items-center gap-1.5 text-sm", children: [_jsx(Calendar, { className: "h-4 w-4" }), "Reported ", formatDate(scammer.createdAt)] }), scammer.amountLost ? (_jsxs(Text, { className: "text-sm font-medium text-[color:var(--appkit-color-danger,theme(colors.red.600))]", children: [formatPaise(scammer.amountLost), " lost (primary incident)"] })) : null] })] }) }), (scammer.phones.length > 0 ||
42
- scammer.upiIds.length > 0 ||
43
- scammer.emails.length > 0) && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Contact Identifiers" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [scammer.phones.map((p) => (_jsx(IdentityChip, { label: "Phone", value: p, icon: _jsx(Phone, { className: "h-4 w-4" }) }, p))), scammer.upiIds.map((u) => (_jsx(IdentityChip, { label: "UPI ID", value: u, icon: _jsx(Wallet, { className: "h-4 w-4" }) }, u))), scammer.emails.map((e) => (_jsx(IdentityChip, { label: "Email", value: e, icon: _jsx(Mail, { className: "h-4 w-4" }) }, e)))] })] })), scammer.socialMedia.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Social Media Profiles" }), _jsx(Stack, { gap: "xs", children: scammer.socialMedia.map((sm, i) => (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(ExternalLink, { className: "h-4 w-4 shrink-0 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" }), _jsxs(Text, { className: "text-sm font-medium", children: [SOCIAL_PLATFORM_LABELS[sm.platform] ?? sm.platform, ":"] }), sm.url ? (_jsx("a", { href: sm.url, target: "_blank", rel: "noopener noreferrer", className: "text-sm text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: sm.handle })) : (_jsx(Text, { variant: "secondary", className: "text-sm", children: sm.handle }))] }, i))) })] })), scammer.itemInvolved && (_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Item Involved" }), _jsx(Text, { variant: "secondary", className: "text-sm", children: scammer.itemInvolved })] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "What Happened" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Text, { className: "text-sm leading-relaxed", children: scammer.description }) })] }), (() => {
44
- const scamTypeDef = getScamType(scammer.scamType);
45
- if (!scamTypeDef || scamTypeDef.howToAvoid.length === 0)
46
- return null;
47
- return (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "How to Avoid This Scam" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Stack, { gap: "xs", as: "ol", children: scamTypeDef.howToAvoid.map((tip, i) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[color:var(--appkit-color-success,theme(colors.green.600))]/10 text-xs font-bold text-[color:var(--appkit-color-success,theme(colors.green.700))]", children: i + 1 }), _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: tip })] }, i))) }) })] }));
48
- })(), scammer.evidence.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Evidence" }), _jsx(Row, { gap: "sm", wrap: true, children: scammer.evidence.map((url, i) => (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: url, alt: `Evidence ${i + 1}`, className: "h-32 w-auto rounded-lg border object-cover shadow-sm hover:opacity-90" }) }, i))) })] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Row, { justify: "between", align: "center", children: _jsxs(Heading, { level: 2, className: "text-base font-semibold", children: ["Additional Incidents", incidents.length > 0 && ` (${incidents.length})`] }) }), incidents.length === 0 ? (_jsx(EmptyState, { icon: _jsx(FileText, { className: "h-8 w-8" }), title: "No additional incidents yet", description: "Other victims' verified reports linked to this profile will appear here." })) : (_jsx(Stack, { gap: "sm", children: incidents.map((inc) => (_jsx(Card, { variant: "outlined", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", className: "flex-wrap", children: [_jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[inc.scamType] ?? inc.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[inc.scamPlatform] ?? inc.scamPlatform] })] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: formatDate(inc.createdAt) })] }), inc.itemInvolved && (_jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Item: ", inc.itemInvolved] })), inc.amountLost ? (_jsxs(Text, { className: "text-xs font-medium text-[color:var(--appkit-color-danger,theme(colors.red.600))]", children: [formatPaise(inc.amountLost), " lost"] })) : null, _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: inc.description.length > 200
49
- ? `${inc.description.slice(0, 200).trimEnd()}…`
50
- : inc.description }), _jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Reported by: ", inc.reportedByAnon ? "Anonymous" : "Verified victim"] })] }) }, inc.id))) }))] }), _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Heading, { level: 2, className: "text-base font-semibold", children: ["Community Discussion", comments.length > 0 && ` (${comments.length})`] }), isAuthenticated ? (_jsx(Link, { href: String(ROUTES.PUBLIC.SCAM_REPORT), className: "text-xs text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: "Leave a comment" })) : (_jsx(Link, { href: `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(`/scams/${scammer.id}`)}`, className: "text-xs text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: "Sign in to comment" }))] }), comments.length === 0 ? (_jsx(EmptyState, { icon: _jsx(MessageSquare, { className: "h-8 w-8" }), title: "No comments yet", description: "Community comments and victim testimonials appear here." })) : (_jsx(Stack, { gap: "sm", children: comments.map((c) => (_jsx(Card, { variant: "flat", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "center", gap: "sm", children: [_jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Text, { className: "text-sm font-medium", children: c.authorDisplayName }), c.authorRole !== "user" && (_jsx(Badge, { variant: "default", className: "text-[10px]", children: c.authorRole })), c.isAccused && c.isAccusedVerified && (_jsx(Badge, { variant: "warning", className: "text-[10px]", children: "Accused" })), c.isVerifiedVictim && (_jsx(Badge, { variant: "success", className: "text-[10px]", children: "Verified Victim" }))] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: formatDate(c.createdAt) })] }), _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: c.body })] }) }, c.id))) }))] }), relatedScammers.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Related Profiles" }), _jsx(Stack, { gap: "sm", children: relatedScammers.map((rel) => (_jsx(Link, { href: String(ROUTES.PUBLIC.SCAM_DETAIL(rel.id)), className: "block", children: _jsx(Card, { variant: "outlined", padding: "sm", className: "hover:opacity-80 transition-opacity", children: _jsxs(Row, { gap: "sm", align: "center", justify: "between", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { className: "text-sm font-medium", children: rel.displayNames[0] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: SCAM_TYPE_LABELS[rel.scamType] ?? rel.scamType })] }), _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Badge, { variant: statusVariant(rel.status), children: SCAMMER_STATUS_LABELS[rel.status] ?? rel.status }), _jsx(Link2, { className: "h-4 w-4 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" })] })] }) }) }, rel.id))) })] }))] }), _jsxs(Stack, { gap: "md", children: [_jsxs(Card, { variant: "elevated", padding: "md", children: [_jsx(CardHeader, { children: _jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide", children: "Actions" }) }), _jsx(CardBody, { children: _jsxs(Stack, { gap: "sm", children: [isAuthenticated ? (_jsxs(Link, { href: reportHref, className: "appkit-button appkit-button--outline appkit-button--md flex w-full items-center gap-2", children: [_jsx(AlertOctagon, { className: "h-4 w-4 text-[color:var(--appkit-color-danger,theme(colors.red.500))]" }), "Report another incident"] })) : (_jsxs(Link, { href: `${String(ROUTES.AUTH.LOGIN)}?redirect=${encodeURIComponent(reportHref)}`, className: "appkit-button appkit-button--outline appkit-button--md flex w-full items-center gap-2", children: [_jsx(AlertOctagon, { className: "h-4 w-4 text-[color:var(--appkit-color-danger,theme(colors.red.500))]" }), "Sign in to report an incident"] })), isAuthenticated ? (_jsxs(Link, { href: `/scams/${scammer.id}/contest`, className: "appkit-button appkit-button--outline appkit-button--md flex w-full items-center gap-2", children: [_jsx(Flag, { className: "h-4 w-4 text-[color:var(--appkit-color-warning,theme(colors.amber.500))]" }), "Contest this profile"] })) : (_jsxs(Link, { href: contestLoginHref, className: "appkit-button appkit-button--ghost appkit-button--md flex w-full items-center gap-2", children: [_jsx(Flag, { className: "h-4 w-4" }), "Sign in to contest"] })), _jsx(Text, { variant: "secondary", className: "text-xs", children: "All contest submissions are reviewed by our moderation team before any changes are made." })] }) })] }), _jsxs(Card, { variant: "outlined", padding: "md", children: [_jsx(CardHeader, { children: _jsx(Heading, { level: 3, className: "text-sm font-semibold uppercase tracking-wide", children: "Contest Options" }) }), _jsx(CardBody, { children: _jsx(Stack, { gap: "xs", as: "ul", children: Object.entries(CONTEST_TYPE_LABELS).map(([, label]) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx("span", { className: "mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-[color:var(--appkit-color-border,theme(colors.zinc.300))]" }), _jsx(Text, { variant: "secondary", className: "text-xs", children: label })] }, label))) }) })] }), scammer.verifiedAt && (_jsx(Alert, { variant: "success", compact: true, children: _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Shield, { className: "h-3.5 w-3.5 shrink-0" }), _jsxs(Text, { className: "text-xs font-medium", children: ["Verified by LetItRip moderation on ", formatDate(scammer.verifiedAt)] })] }) }))] })] }) }) })] }));
59
+ ] }) }) }), _jsx(Section, { className: "py-10", children: _jsx(Container, { size: "xl", children: _jsxs(Grid, { cols: "twoThird", gap: "lg", children: [_jsxs(Stack, { gap: "lg", children: [_jsx(ScammerHeaderCard, { scammer: scammer }), (scammer.phones.length > 0 || scammer.upiIds.length > 0 || scammer.emails.length > 0) && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Contact Identifiers" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [scammer.phones.map((p) => (_jsx(IdentityChip, { label: "Phone", value: p, icon: _jsx(Phone, { className: "h-4 w-4" }) }, p))), scammer.upiIds.map((u) => (_jsx(IdentityChip, { label: "UPI ID", value: u, icon: _jsx(Wallet, { className: "h-4 w-4" }) }, u))), scammer.emails.map((e) => (_jsx(IdentityChip, { label: "Email", value: e, icon: _jsx(Mail, { className: "h-4 w-4" }) }, e)))] })] })), scammer.socialMedia.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Social Media Profiles" }), _jsx(Stack, { gap: "xs", children: scammer.socialMedia.map((sm, i) => (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(ExternalLink, { className: "h-4 w-4 shrink-0 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" }), _jsxs(Text, { className: "text-sm font-medium", children: [SOCIAL_PLATFORM_LABELS[sm.platform] ?? sm.platform, ":"] }), sm.url ? (_jsx("a", { href: sm.url, target: "_blank", rel: "noopener noreferrer", className: "text-sm text-[color:var(--appkit-color-primary,theme(colors.blue.600))] hover:underline", children: sm.handle })) : (_jsx(Text, { variant: "secondary", className: "text-sm", children: sm.handle }))] }, i))) })] })), scammer.itemInvolved && (_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Item Involved" }), _jsx(Text, { variant: "secondary", className: "text-sm", children: scammer.itemInvolved })] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "What Happened" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Text, { className: "text-sm leading-relaxed", children: scammer.description }) })] }), scamTypeDef && scamTypeDef.howToAvoid.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "How to Avoid This Scam" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Stack, { gap: "xs", as: "ol", children: scamTypeDef.howToAvoid.map((tip, i) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[color:var(--appkit-color-success,theme(colors.green.600))]/10 text-xs font-bold text-[color:var(--appkit-color-success,theme(colors.green.700))]", children: i + 1 }), _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: tip })] }, i))) }) })] })), scammer.evidence.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Evidence" }), _jsx(Row, { gap: "sm", wrap: true, children: scammer.evidence.map((url, i) => (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: url, alt: `Evidence ${i + 1}`, className: "h-32 w-auto rounded-lg border object-cover shadow-sm hover:opacity-90" }) }, i))) })] })), _jsx(ScammerIncidentsSection, { incidents: incidents }), _jsx(ScammerCommentsSection, { comments: comments, isAuthenticated: isAuthenticated, scammerId: scammer.id }), relatedScammers.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: "Related Profiles" }), _jsx(Stack, { gap: "sm", children: relatedScammers.map((rel) => (_jsx(Link, { href: String(ROUTES.PUBLIC.SCAM_DETAIL(rel.id)), className: "block", children: _jsx(Card, { variant: "outlined", padding: "sm", className: "hover:opacity-80 transition-opacity", children: _jsxs(Row, { gap: "sm", align: "center", justify: "between", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { className: "text-sm font-medium", children: rel.displayNames[0] }), _jsx(Text, { variant: "secondary", className: "text-xs", children: SCAM_TYPE_LABELS[rel.scamType] ?? rel.scamType })] }), _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Badge, { variant: statusVariant(rel.status), children: SCAMMER_STATUS_LABELS[rel.status] ?? rel.status }), _jsx(Link2, { className: "h-4 w-4 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" })] })] }) }) }, rel.id))) })] }))] }), _jsx(ScammerActionsColumn, { scammer: scammer, isAuthenticated: isAuthenticated, reportHref: reportHref, contestLoginHref: contestLoginHref })] }) }) })] }));
51
60
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
+ const CLS_ICON_SM = "h-3 w-3 shrink-0";
3
4
  import { Shield, Phone, Wallet, Mail, ChevronRight, Search } from "lucide-react";
4
5
  import { Container, Heading, Main, Section, Text, Grid, Stack, Row, Card, Badge, Alert, EmptyState, Input, } from "../../../ui";
5
6
  import { listVerifiedScammers } from "../actions/scam-actions";
@@ -19,7 +20,7 @@ function ScammerCard({ scammer }) {
19
20
  const href = String(ROUTES.PUBLIC.SCAM_DETAIL(scammer.id));
20
21
  const name = scammer.displayNames[0] ?? "Unknown";
21
22
  const aliases = scammer.displayNames.slice(1);
22
- return (_jsx(Link, { href: href, className: "group block", children: _jsx(Card, { variant: "outlined", hover: true, padding: "md", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", gap: "sm", align: "start", children: [_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Text, { className: "truncate font-semibold", children: name }), aliases.length > 0 && (_jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Also known as: ", aliases.join(", ")] }))] }), _jsx(Badge, { variant: "danger", children: "Verified" })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsx(Badge, { variant: "default", children: SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform })] }), _jsxs(Stack, { gap: "xs", children: [scammer.phones.slice(0, 2).map((p) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Phone, { className: "h-3 w-3 shrink-0" }), " ", p] }, p))), scammer.upiIds.slice(0, 1).map((u) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Wallet, { className: "h-3 w-3 shrink-0" }), " ", u] }, u))), scammer.emails.slice(0, 1).map((e) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Mail, { className: "h-3 w-3 shrink-0" }), " ", e] }, e)))] }), _jsxs(Row, { justify: "between", gap: "sm", children: [_jsxs(Text, { variant: "secondary", className: "text-xs", children: [scammer.incidentCount > 0
23
+ return (_jsx(Link, { href: href, className: "group block", children: _jsx(Card, { variant: "outlined", hover: true, padding: "md", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", gap: "sm", align: "start", children: [_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsx(Text, { className: "truncate font-semibold", children: name }), aliases.length > 0 && (_jsxs(Text, { variant: "secondary", className: "text-xs", children: ["Also known as: ", aliases.join(", ")] }))] }), _jsx(Badge, { variant: "danger", children: "Verified" })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsx(Badge, { variant: "default", children: SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform })] }), _jsxs(Stack, { gap: "xs", children: [scammer.phones.slice(0, 2).map((p) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Phone, { className: CLS_ICON_SM }), " ", p] }, p))), scammer.upiIds.slice(0, 1).map((u) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Wallet, { className: CLS_ICON_SM }), " ", u] }, u))), scammer.emails.slice(0, 1).map((e) => (_jsxs(Text, { variant: "secondary", className: "flex items-center gap-1 text-xs", children: [_jsx(Mail, { className: CLS_ICON_SM }), " ", e] }, e)))] }), _jsxs(Row, { justify: "between", gap: "sm", children: [_jsxs(Text, { variant: "secondary", className: "text-xs", children: [scammer.incidentCount > 0
23
24
  ? `${scammer.incidentCount + 1} victims reported`
24
25
  : "1 victim reported", scammer.amountLost ? ` · ${formatPaise(scammer.amountLost)} lost` : ""] }), _jsx(ChevronRight, { className: "h-4 w-4 shrink-0 text-zinc-300 transition group-hover:text-red-500 dark:text-zinc-600" })] })] }) }) }));
25
26
  }
@@ -1,6 +1,6 @@
1
1
  import type { LucideIcon } from "lucide-react";
2
2
  import type { NavSuggestionRecord } from "../hooks/useNavSuggestions";
3
- export type SearchResourceType = "products" | "auctions" | "pre-orders" | "prize-draws" | "bundles" | "stores" | "categories" | "brands" | "events" | "blog" | "faqs";
3
+ export type SearchResourceType = "products" | "auctions" | "pre-orders" | "prize-draws" | "bundles" | "classified" | "digital-codes" | "live" | "stores" | "categories" | "brands" | "events" | "blog" | "faqs";
4
4
  export interface SearchResourceTypeOption {
5
5
  value: SearchResourceType;
6
6
  label: string;