@mohasinac/appkit 2.7.27 → 2.7.30

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 (359) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/server/features/checkout/actions.js +42 -35
  3. package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
  4. package/dist/_internal/server/features/classified/adapters.js +24 -0
  5. package/dist/_internal/server/features/classified/index.d.ts +3 -0
  6. package/dist/_internal/server/features/classified/index.js +3 -0
  7. package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
  8. package/dist/_internal/server/features/classified/metadata.js +43 -0
  9. package/dist/_internal/server/features/classified/og.d.ts +30 -0
  10. package/dist/_internal/server/features/classified/og.js +76 -0
  11. package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
  12. package/dist/_internal/server/features/digital-code/adapters.js +29 -0
  13. package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
  14. package/dist/_internal/server/features/digital-code/index.js +3 -0
  15. package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
  16. package/dist/_internal/server/features/digital-code/metadata.js +36 -0
  17. package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
  18. package/dist/_internal/server/features/digital-code/og.js +77 -0
  19. package/dist/_internal/server/features/live/adapters.d.ts +21 -0
  20. package/dist/_internal/server/features/live/adapters.js +34 -0
  21. package/dist/_internal/server/features/live/index.d.ts +3 -0
  22. package/dist/_internal/server/features/live/index.js +3 -0
  23. package/dist/_internal/server/features/live/metadata.d.ts +8 -0
  24. package/dist/_internal/server/features/live/metadata.js +39 -0
  25. package/dist/_internal/server/features/live/og.d.ts +27 -0
  26. package/dist/_internal/server/features/live/og.js +72 -0
  27. package/dist/_internal/server/features/promotions/actions.js +4 -3
  28. package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
  29. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
  30. package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
  31. package/dist/_internal/shared/actions/action-registry.js +310 -8
  32. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  33. package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
  34. package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
  35. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  36. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
  37. package/dist/_internal/shared/features/events/schema.d.ts +8 -8
  38. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  39. package/dist/_internal/shared/features/products/schema.d.ts +42 -42
  40. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  41. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  42. package/dist/_internal/shared/media/limits.js +4 -3
  43. package/dist/client.d.ts +2 -0
  44. package/dist/client.js +1 -0
  45. package/dist/configs/next.js +4 -0
  46. package/dist/constants/table-keys.d.ts +6 -0
  47. package/dist/constants/table-keys.js +9 -0
  48. package/dist/core/hooks/useSyncManager.js +9 -8
  49. package/dist/features/about/components/FeesView.js +10 -7
  50. package/dist/features/about/components/HelpPageView.js +10 -1
  51. package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
  52. package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
  53. package/dist/features/about/components/HowOffersWorkView.js +10 -1
  54. package/dist/features/about/components/HowOrdersWorkView.js +10 -1
  55. package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
  56. package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
  57. package/dist/features/about/components/HowReviewsWorkView.js +10 -1
  58. package/dist/features/about/components/PublicProfileView.js +44 -14
  59. package/dist/features/about/components/SecurityPrivacyView.js +7 -1
  60. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  61. package/dist/features/account/components/NotificationBell.js +27 -16
  62. package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
  63. package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
  64. package/dist/features/account/components/UserSidebar.js +4 -4
  65. package/dist/features/account/components/UserSupportView.js +16 -6
  66. package/dist/features/account/components/index.d.ts +2 -0
  67. package/dist/features/account/components/index.js +1 -0
  68. package/dist/features/account/schemas/index.d.ts +4 -4
  69. package/dist/features/account/types/index.d.ts +34 -0
  70. package/dist/features/admin/actions/admin-actions.js +4 -3
  71. package/dist/features/admin/actions/notification-actions.d.ts +24 -22
  72. package/dist/features/admin/actions/notification-actions.js +122 -22
  73. package/dist/features/admin/components/AdminAdEditorView.js +29 -15
  74. package/dist/features/admin/components/AdminAdsView.js +22 -28
  75. package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
  76. package/dist/features/admin/components/AdminBidsView.js +10 -3
  77. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  78. package/dist/features/admin/components/AdminBlogView.js +8 -3
  79. package/dist/features/admin/components/AdminBrandsView.js +7 -2
  80. package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
  81. package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
  82. package/dist/features/admin/components/AdminCarouselView.js +13 -7
  83. package/dist/features/admin/components/AdminCategoriesView.js +8 -3
  84. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  85. package/dist/features/admin/components/AdminContactView.js +6 -1
  86. package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
  87. package/dist/features/admin/components/AdminCouponsView.js +6 -1
  88. package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
  89. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  90. package/dist/features/admin/components/AdminFaqsView.js +7 -2
  91. package/dist/features/admin/components/AdminFeaturesView.js +4 -3
  92. package/dist/features/admin/components/AdminMediaView.js +9 -8
  93. package/dist/features/admin/components/AdminNavigationView.js +10 -17
  94. package/dist/features/admin/components/AdminNewsletterView.js +6 -1
  95. package/dist/features/admin/components/AdminNotificationsView.js +11 -3
  96. package/dist/features/admin/components/AdminOrdersView.js +6 -1
  97. package/dist/features/admin/components/AdminPayoutsView.js +10 -3
  98. package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
  99. package/dist/features/admin/components/AdminProductsView.js +18 -12
  100. package/dist/features/admin/components/AdminReviewsView.js +33 -24
  101. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  102. package/dist/features/admin/components/AdminScammersView.js +28 -26
  103. package/dist/features/admin/components/AdminSectionsView.js +112 -99
  104. package/dist/features/admin/components/AdminSessionsView.js +10 -3
  105. package/dist/features/admin/components/AdminSidebar.js +4 -4
  106. package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
  107. package/dist/features/admin/components/AdminStoresView.js +6 -1
  108. package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
  109. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  110. package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
  111. package/dist/features/admin/components/AdminTeamView.js +6 -1
  112. package/dist/features/admin/components/AdminUserEditorView.js +8 -15
  113. package/dist/features/admin/components/AdminUsersView.js +6 -1
  114. package/dist/features/admin/components/AdminViewCards.js +4 -3
  115. package/dist/features/admin/components/DashboardStats.js +2 -2
  116. package/dist/features/admin/components/DataTable.js +2 -2
  117. package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
  118. package/dist/features/admin/repository/chat.repository.js +8 -8
  119. package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
  120. package/dist/features/admin/repository/site-settings.repository.js +11 -9
  121. package/dist/features/admin/schemas/firestore.d.ts +38 -0
  122. package/dist/features/admin/schemas/firestore.js +15 -0
  123. package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
  124. package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
  125. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
  126. package/dist/features/auctions/components/index.d.ts +0 -1
  127. package/dist/features/auctions/components/index.js +0 -1
  128. package/dist/features/auctions/schemas/index.d.ts +14 -14
  129. package/dist/features/auth/actions/realtime-token-actions.js +19 -4
  130. package/dist/features/auth/components/RegisterForm.js +8 -4
  131. package/dist/features/auth/consent-otp.d.ts +2 -0
  132. package/dist/features/auth/consent-otp.js +8 -0
  133. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  134. package/dist/features/auth/schemas/firestore.js +18 -25
  135. package/dist/features/auth/schemas/index.d.ts +2 -2
  136. package/dist/features/before-after/api/[id]/route.js +8 -6
  137. package/dist/features/before-after/schemas/index.d.ts +2 -2
  138. package/dist/features/blog/actions/blog-actions.d.ts +3 -3
  139. package/dist/features/blog/components/BlogIndexListing.js +21 -2
  140. package/dist/features/blog/components/BlogPostForm.js +11 -5
  141. package/dist/features/blog/components/BlogPostView.js +10 -2
  142. package/dist/features/blog/schemas/index.d.ts +18 -18
  143. package/dist/features/cart/components/CartDrawer.js +3 -2
  144. package/dist/features/cart/repository/cart.repository.js +4 -3
  145. package/dist/features/cart/utils/pending-ops.d.ts +1 -1
  146. package/dist/features/categories/api/[id]/route.js +4 -3
  147. package/dist/features/categories/api/route.js +4 -3
  148. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  149. package/dist/features/categories/components/BrandDetailTabs.js +2 -2
  150. package/dist/features/categories/components/BundleCollage.d.ts +8 -0
  151. package/dist/features/categories/components/BundleCollage.js +50 -0
  152. package/dist/features/categories/components/BundleDetailView.js +2 -10
  153. package/dist/features/categories/components/BundleItemsPicker.js +20 -13
  154. package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
  155. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  156. package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
  157. package/dist/features/categories/components/CategoryProductsListing.js +52 -13
  158. package/dist/features/categories/components/index.d.ts +2 -0
  159. package/dist/features/categories/components/index.js +1 -0
  160. package/dist/features/categories/schemas/index.d.ts +6 -6
  161. package/dist/features/checkout/actions/checkout-actions.js +4 -3
  162. package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
  163. package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
  164. package/dist/features/classified/components/ClassifiedFilters.js +19 -0
  165. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
  166. package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
  167. package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
  168. package/dist/features/classified/components/ClassifiedListView.js +43 -0
  169. package/dist/features/classified/components/index.d.ts +6 -0
  170. package/dist/features/classified/components/index.js +3 -0
  171. package/dist/features/collections/api/[slug]/route.js +16 -12
  172. package/dist/features/collections/schemas/index.d.ts +5 -5
  173. package/dist/features/consultation/api/[id]/route.js +12 -9
  174. package/dist/features/consultation/components/ConsultationForm.js +3 -2
  175. package/dist/features/consultation/schemas/index.d.ts +6 -6
  176. package/dist/features/contact/email.js +77 -116
  177. package/dist/features/copilot/components/AdminCopilotView.js +12 -2
  178. package/dist/features/corporate/api/[id]/route.js +12 -9
  179. package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
  180. package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
  181. package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
  182. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
  183. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
  184. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
  185. package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
  186. package/dist/features/digital-codes/components/index.d.ts +6 -0
  187. package/dist/features/digital-codes/components/index.js +3 -0
  188. package/dist/features/events/actions/event-actions.js +4 -3
  189. package/dist/features/events/components/AdminEventEditorView.js +3 -2
  190. package/dist/features/events/components/AdminEventsView.js +4 -2
  191. package/dist/features/events/components/EventPollWidget.js +5 -5
  192. package/dist/features/events/components/EventsIndexListing.js +2 -2
  193. package/dist/features/events/schemas/index.d.ts +20 -20
  194. package/dist/features/faq/actions/faq-actions.d.ts +8 -8
  195. package/dist/features/faq/components/ContactCTA.js +3 -1
  196. package/dist/features/faq/components/FAQAccordion.js +1 -1
  197. package/dist/features/faq/schemas/index.d.ts +4 -4
  198. package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
  199. package/dist/features/filters/AsyncFacetSection.js +65 -0
  200. package/dist/features/forms/Checkbox.js +2 -2
  201. package/dist/features/forms/Input.js +2 -2
  202. package/dist/features/forms/Radio.js +5 -5
  203. package/dist/features/forms/Select.js +2 -2
  204. package/dist/features/forms/Slider.js +2 -2
  205. package/dist/features/forms/Textarea.js +2 -2
  206. package/dist/features/homepage/api/[id]/route.js +4 -3
  207. package/dist/features/homepage/api/sections/[id]/route.js +12 -9
  208. package/dist/features/homepage/components/AdSlot.js +2 -1
  209. package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
  210. package/dist/features/homepage/components/BrandsSection.js +2 -2
  211. package/dist/features/homepage/components/CTABannerSection.js +2 -2
  212. package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
  213. package/dist/features/homepage/components/CharacterHotspot.js +119 -157
  214. package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
  215. package/dist/features/homepage/components/CustomCardsSection.js +8 -6
  216. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  217. package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
  218. package/dist/features/homepage/components/HeroBanner.js +5 -4
  219. package/dist/features/homepage/components/HeroCarousel.js +13 -26
  220. package/dist/features/homepage/components/HowItWorksSection.js +4 -2
  221. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  222. package/dist/features/homepage/components/SocialPostCard.js +3 -2
  223. package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
  224. package/dist/features/layout/AppLayoutShell.d.ts +8 -2
  225. package/dist/features/layout/AppLayoutShell.js +54 -78
  226. package/dist/features/layout/BottomActions.js +43 -48
  227. package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
  228. package/dist/features/live/components/LiveItemFilters.js +23 -0
  229. package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
  230. package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
  231. package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
  232. package/dist/features/live/components/LiveItemsListView.js +43 -0
  233. package/dist/features/live/components/index.d.ts +6 -0
  234. package/dist/features/live/components/index.js +3 -0
  235. package/dist/features/media/MediaLightbox.js +8 -14
  236. package/dist/features/media/types/index.d.ts +2 -2
  237. package/dist/features/media/upload/CameraCapture.js +2 -2
  238. package/dist/features/media/upload/ImageUpload.js +4 -2
  239. package/dist/features/media/upload/MediaUploadField.js +24 -8
  240. package/dist/features/media/upload/MediaUploadList.js +1 -1
  241. package/dist/features/orders/actions/order-actions.js +4 -3
  242. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
  243. package/dist/features/orders/components/OrderFilters.js +12 -9
  244. package/dist/features/orders/schemas/index.d.ts +6 -6
  245. package/dist/features/pre-orders/api/[id]/route.js +12 -9
  246. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
  247. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
  248. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
  249. package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
  250. package/dist/features/pre-orders/components/PreorderCard.js +2 -13
  251. package/dist/features/pre-orders/components/index.d.ts +1 -1
  252. package/dist/features/pre-orders/components/index.js +1 -1
  253. package/dist/features/pre-orders/schemas/index.d.ts +10 -10
  254. package/dist/features/products/api/[id]/route.js +9 -7
  255. package/dist/features/products/api/route.js +1 -1
  256. package/dist/features/products/columns/productTableColumns.js +2 -2
  257. package/dist/features/products/components/AuctionsIndexListing.js +15 -51
  258. package/dist/features/products/components/BidHistory.js +2 -2
  259. package/dist/features/products/components/CompareOverlay.js +3 -2
  260. package/dist/features/products/components/MakeOfferButton.js +20 -14
  261. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
  262. package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
  263. package/dist/features/products/components/PrizeDrawCollage.js +54 -14
  264. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  265. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  266. package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
  267. package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
  268. package/dist/features/products/components/PrizeRevealModal.js +24 -14
  269. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  270. package/dist/features/products/components/ProductFilters.js +8 -6
  271. package/dist/features/products/components/ProductForm.js +12 -10
  272. package/dist/features/products/components/ProductGalleryClient.js +2 -1
  273. package/dist/features/products/components/ProductGrid.js +11 -14
  274. package/dist/features/products/components/ProductsIndexListing.js +16 -52
  275. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  276. package/dist/features/products/schemas/index.d.ts +56 -56
  277. package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
  278. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  279. package/dist/features/promotions/schemas/index.d.ts +6 -6
  280. package/dist/features/reviews/actions/review-actions.js +6 -5
  281. package/dist/features/reviews/api/[id]/route.js +4 -3
  282. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  283. package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
  284. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  285. package/dist/features/reviews/schemas/index.d.ts +6 -6
  286. package/dist/features/scams/components/ScamProfileView.js +19 -10
  287. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  288. package/dist/features/search/components/Search.d.ts +1 -1
  289. package/dist/features/search/components/Search.js +56 -49
  290. package/dist/features/search/schemas/index.d.ts +5 -5
  291. package/dist/features/seller/actions/offer-actions.js +11 -9
  292. package/dist/features/seller/components/SellerAddressesView.js +6 -4
  293. package/dist/features/seller/components/SellerBidsView.js +2 -2
  294. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  295. package/dist/features/seller/components/SellerOffersPanel.js +6 -1
  296. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  297. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
  298. package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
  299. package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
  300. package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
  301. package/dist/features/seller/components/SellerProductShell.js +75 -9
  302. package/dist/features/seller/components/SellerProductsView.js +10 -11
  303. package/dist/features/seller/components/SellerShippingView.js +3 -3
  304. package/dist/features/seller/components/SellerSidebar.js +10 -7
  305. package/dist/features/seller/components/SellerStorefrontView.js +3 -3
  306. package/dist/features/seller/schemas/index.d.ts +4 -4
  307. package/dist/features/shell/FormShell.js +2 -1
  308. package/dist/features/shell/QuickFormDrawer.js +4 -3
  309. package/dist/features/shell/StepForm.js +4 -3
  310. package/dist/features/stores/actions/store-query-actions.js +4 -3
  311. package/dist/features/stores/components/StoreAboutView.js +2 -1
  312. package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
  313. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
  314. package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
  315. package/dist/features/stores/components/StoreProductsListing.js +16 -49
  316. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  317. package/dist/features/stores/schemas/index.d.ts +2 -2
  318. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
  319. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
  320. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
  321. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
  322. package/dist/index.d.ts +21 -3
  323. package/dist/index.js +1737 -1725
  324. package/dist/monitoring/server-logger.js +5 -9
  325. package/dist/next/api/apiHandler.js +56 -37
  326. package/dist/next/components/GlobalError.js +2 -1
  327. package/dist/next/routing/route-map.d.ts +27 -0
  328. package/dist/next/routing/route-map.js +10 -0
  329. package/dist/react/ErrorBoundary.js +2 -1
  330. package/dist/react/contexts/SessionContext.js +38 -28
  331. package/dist/react/hooks/useCamera.d.ts +1 -1
  332. package/dist/react/hooks/useCamera.js +8 -7
  333. package/dist/seed/site-settings-seed-data.js +6 -0
  334. package/dist/server-entry.d.ts +3 -0
  335. package/dist/server-entry.js +3 -0
  336. package/dist/server.d.ts +12 -0
  337. package/dist/server.js +15 -0
  338. package/dist/tailwind-utilities.css +1 -1
  339. package/dist/ui/DataTable.js +28 -7
  340. package/dist/ui/components/Button.d.ts +8 -1
  341. package/dist/ui/components/Button.js +47 -8
  342. package/dist/ui/components/CollageLightbox.d.ts +18 -0
  343. package/dist/ui/components/CollageLightbox.js +41 -0
  344. package/dist/ui/components/Dropdown.js +7 -6
  345. package/dist/ui/components/ImageLightbox.d.ts +7 -1
  346. package/dist/ui/components/ImageLightbox.js +15 -5
  347. package/dist/ui/components/PageLoader.js +4 -3
  348. package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
  349. package/dist/ui/components/PaginatedMultiSelect.js +141 -0
  350. package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
  351. package/dist/ui/components/Semantic.js +1 -1
  352. package/dist/ui/components/SideDrawer.js +17 -15
  353. package/dist/ui/components/index.style.css +1 -0
  354. package/dist/ui/index.d.ts +2 -0
  355. package/dist/ui/index.js +1 -0
  356. package/dist/utils/number.formatter.js +5 -13
  357. package/dist/utils/string.formatter.js +21 -18
  358. package/dist/validation/schemas.d.ts +2 -2
  359. package/package.json +1 -1
@@ -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;
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
+ const SVG_PATH_SEARCH = "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z";
5
+ const CLS_HOVER_ROW = "hover:bg-zinc-100 dark:hover:bg-slate-800";
6
+ const CLS_ICON_ROW = "w-4 h-4 flex-shrink-0 text-zinc-500 dark:text-zinc-400";
7
+ const CLS_TRUNCATE = "font-medium truncate";
4
8
  import { Button, Input, Li, Row, Span, Text, Ul } from "../../../ui";
5
9
  import { useNavSuggestions } from "../hooks/useNavSuggestions";
6
10
  const DEFAULT_TYPE_ICONS = {
@@ -15,6 +19,10 @@ const DEFAULT_TYPE_BADGES = {
15
19
  blog: "bg-fuchsia-100 text-fuchsia-600 dark:bg-fuchsia-900/40 dark:text-fuchsia-300",
16
20
  event: "bg-amber-100 text-amber-600 dark:bg-amber-900/40 dark:text-amber-300",
17
21
  };
22
+ function SuggestionItem({ suggestion, itemIndex, activeIndex, onSelect, onHover, typeIcons, typeBadges, }) {
23
+ const isActive = activeIndex === itemIndex;
24
+ return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => onSelect(suggestion), onMouseEnter: () => onHover(itemIndex), className: `w-full flex items-center gap-3 px-4 py-3 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 last:border-b-0 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}`, children: [_jsx(Span, { className: "text-sm", children: typeIcons[suggestion.type] }), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "search-div-441", children: [_jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] }), _jsx(Span, { className: `text-xs px-2 py-0.5 rounded-full ${typeBadges[suggestion.type]}`, children: suggestion.type })] }) }));
25
+ }
18
26
  export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred = true, debounceMs = 300, onClear, className, router, quickLinks = [], labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType = "products", storageKey = "letitrip_search_type", }) {
19
27
  const isInlineMode = value !== undefined;
20
28
  const [query, setQuery] = useState(isInlineMode ? value : "");
@@ -144,38 +152,47 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
144
152
  handleDeferredSubmit();
145
153
  setIsInlineOpen(false);
146
154
  };
147
- return (_jsxs(Row, { className: `relative gap-2 ${className ?? ""}`, children: [_jsxs(Row, { className: "relative flex-1", children: [_jsx("svg", { className: "absolute left-3 w-4 h-4 text-zinc-400 pointer-events-none", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Input, { ref: inputRef, bare: true, type: "search", value: query, onChange: (event) => handleInlineChange(event.target.value), onKeyDown: (event) => {
148
- if (event.key === "ArrowDown") {
149
- if (inlineItems.length === 0)
150
- return;
151
- event.preventDefault();
152
- setIsInlineOpen(true);
153
- setActiveIndex((current) => current < inlineItems.length - 1 ? current + 1 : 0);
154
- return;
155
- }
156
- if (event.key === "ArrowUp") {
157
- if (inlineItems.length === 0)
158
- return;
159
- event.preventDefault();
160
- setIsInlineOpen(true);
161
- setActiveIndex((current) => current <= 0 ? inlineItems.length - 1 : current - 1);
162
- return;
163
- }
164
- if (event.key === "Enter") {
165
- if (activeIndex >= 0) {
166
- event.preventDefault();
167
- handleInlineActiveItem(activeIndex);
168
- return;
169
- }
170
- if (deferred) {
171
- handleDeferredSubmit();
172
- }
173
- return;
174
- }
175
- if (event.key === "Escape") {
176
- setIsInlineOpen(false);
177
- }
178
- }, onFocus: () => {
155
+ const renderInlineQuickLink = (link, index) => {
156
+ const Icon = link.icon;
157
+ const isActive = activeIndex === index;
158
+ return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
159
+ setIsInlineOpen(false);
160
+ router.push(link.href);
161
+ }, onMouseEnter: () => setActiveIndex(index), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}`, children: [_jsx(Icon, { className: CLS_ICON_ROW }), _jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: link.label })] }) }, `inline-link-${link.href}`));
162
+ };
163
+ const handleInlineKeyDown = (event) => {
164
+ if (event.key === "ArrowDown") {
165
+ if (inlineItems.length === 0)
166
+ return;
167
+ event.preventDefault();
168
+ setIsInlineOpen(true);
169
+ setActiveIndex((current) => current < inlineItems.length - 1 ? current + 1 : 0);
170
+ return;
171
+ }
172
+ if (event.key === "ArrowUp") {
173
+ if (inlineItems.length === 0)
174
+ return;
175
+ event.preventDefault();
176
+ setIsInlineOpen(true);
177
+ setActiveIndex((current) => current <= 0 ? inlineItems.length - 1 : current - 1);
178
+ return;
179
+ }
180
+ if (event.key === "Enter") {
181
+ if (activeIndex >= 0) {
182
+ event.preventDefault();
183
+ handleInlineActiveItem(activeIndex);
184
+ return;
185
+ }
186
+ if (deferred) {
187
+ handleDeferredSubmit();
188
+ }
189
+ return;
190
+ }
191
+ if (event.key === "Escape") {
192
+ setIsInlineOpen(false);
193
+ }
194
+ };
195
+ return (_jsxs(Row, { className: `relative gap-2 ${className ?? ""}`, children: [_jsxs(Row, { className: "relative flex-1", children: [_jsx("svg", { className: "absolute left-3 w-4 h-4 text-zinc-400 pointer-events-none", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Input, { ref: inputRef, bare: true, type: "search", value: query, onChange: (event) => handleInlineChange(event.target.value), onKeyDown: handleInlineKeyDown, onFocus: () => {
179
196
  if (inlineBlurRef.current)
180
197
  clearTimeout(inlineBlurRef.current);
181
198
  setIsInlineOpen(true);
@@ -183,22 +200,15 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
183
200
  if (inlineBlurRef.current)
184
201
  clearTimeout(inlineBlurRef.current);
185
202
  inlineBlurRef.current = setTimeout(() => setIsInlineOpen(false), 120);
186
- }, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-white pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-0.5 rounded-full text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx("select", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), deferred && (_jsx(Button, { type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0 px-3 py-2 rounded-lg", children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs("div", { className: "absolute top-full left-0 right-0 mt-2 rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", style: { zIndex: "var(--appkit-z-dropdown)" }, onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => {
187
- const Icon = link.icon;
188
- const isActive = activeIndex === index;
189
- return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
190
- setIsInlineOpen(false);
191
- router.push(link.href);
192
- }, onMouseEnter: () => setActiveIndex(index), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}`, children: [_jsx(Icon, { className: "w-4 h-4 flex-shrink-0 text-zinc-500 dark:text-zinc-400" }), _jsx(Text, { size: "sm", className: "font-medium truncate", children: link.label })] }) }, `inline-link-${link.href}`));
193
- }) })), query && suggestionsLoading && (_jsx("div", { className: "px-4 py-3", "data-section": "search-div-431", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
203
+ }, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-white pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-0.5 rounded-full text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx("select", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), deferred && (_jsx(Button, { type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0 px-3 py-2 rounded-lg", children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs("div", { className: "absolute top-full left-0 right-0 mt-2 rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", style: { zIndex: "var(--appkit-z-dropdown)" }, onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => renderInlineQuickLink(link, index)) })), query && suggestionsLoading && (_jsx("div", { className: "px-4 py-3", "data-section": "search-div-431", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
194
204
  suggestions.slice(0, 5).map((suggestion, suggestionIndex) => {
195
205
  const itemIndex = inlineQuickLinkItems.length + suggestionIndex;
196
206
  const isActive = activeIndex === itemIndex;
197
- return (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => handleSuggestionClick(suggestion), onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}`, children: [_jsx(Span, { className: "text-sm", children: typeIcons[suggestion.type] }), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "search-div-432", children: [_jsx(Text, { size: "sm", className: "font-medium truncate", children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] })] }, `inline-suggestion-${suggestion.objectID}`));
207
+ return (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => handleSuggestionClick(suggestion), onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}`, children: [_jsx(Span, { className: "text-sm", children: typeIcons[suggestion.type] }), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "search-div-432", children: [_jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] })] }, `inline-suggestion-${suggestion.objectID}`));
198
208
  }), query.trim() && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
199
209
  handleDeferredSubmit();
200
210
  setIsInlineOpen(false);
201
- }, onMouseEnter: () => setActiveIndex(inlineItems.length - 1), className: `w-full flex items-center gap-2 px-4 py-3 text-left transition-colors ${activeIndex === inlineItems.length - 1 ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}`, children: [_jsx("svg", { className: "w-4 h-4 flex-shrink-0 text-zinc-500 dark:text-zinc-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Text, { size: "sm", className: "font-medium truncate", children: labels.browseProducts(query) })] }))] }))] }));
211
+ }, onMouseEnter: () => setActiveIndex(inlineItems.length - 1), className: `w-full flex items-center gap-2 px-4 py-3 text-left transition-colors ${activeIndex === inlineItems.length - 1 ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}`, children: [_jsx("svg", { className: CLS_ICON_ROW, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: labels.browseProducts(query) })] }))] }))] }));
202
212
  }
203
213
  const overlayQuery = query.toLowerCase();
204
214
  const filteredSiteLinks = query
@@ -220,6 +230,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
220
230
  ...suggestionItems,
221
231
  ...(query.trim() ? [{ kind: "search" }] : []),
222
232
  ];
233
+ const getOverlaySuggestionIndex = (suggestionIndex) => quickLinkItems.length + suggestionIndex;
223
234
  const handleActiveItem = (index) => {
224
235
  const item = overlayItems[index];
225
236
  if (!item)
@@ -265,17 +276,13 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
265
276
  };
266
277
  if (!isOpen)
267
278
  return null;
268
- return (_jsx("div", { className: "border-b border-zinc-200 bg-white shadow-md dark:border-slate-800 dark:bg-slate-950 animate-[fadeIn_150ms_ease-out]", "data-section": "search-div-433", children: _jsxs("div", { className: "container mx-auto px-4 py-3 md:py-4 relative", "data-section": "search-div-434", children: [_jsxs(Row, { gap: "sm", className: "md:gap-3", children: [_jsx("div", { className: "flex-1 relative", "data-section": "search-div-435", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-white text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx("select", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), _jsxs(Button, { onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:flex gap-2", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), labels.title] }), _jsx(Button, { variant: "ghost", onClick: () => onClose?.(), className: "p-2.5 md:p-3 rounded-xl transition-colors text-zinc-500 hover:bg-zinc-100 hover:text-zinc-700 dark:text-zinc-400 dark:hover:bg-slate-800 dark:hover:text-zinc-200 flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs("div", { className: "absolute top-full left-0 right-0 px-4 pt-2 pb-4 space-y-2 z-50", "data-section": "search-div-436", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs("div", { className: "rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", "data-section": "search-div-437", children: [_jsx("div", { className: "px-4 py-2 border-b border-zinc-200 dark:border-slate-700", "data-section": "search-div-438", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider font-semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
279
+ return (_jsx("div", { className: "border-b border-zinc-200 bg-white shadow-md dark:border-slate-800 dark:bg-slate-950 animate-[fadeIn_150ms_ease-out]", "data-section": "search-div-433", children: _jsxs("div", { className: "container mx-auto px-4 py-3 md:py-4 relative", "data-section": "search-div-434", children: [_jsxs(Row, { gap: "sm", className: "md:gap-3", children: [_jsx("div", { className: "flex-1 relative", "data-section": "search-div-435", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-white text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx("select", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), _jsxs(Button, { onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:flex gap-2", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), labels.title] }), _jsx(Button, { variant: "ghost", onClick: () => onClose?.(), className: "p-2.5 md:p-3 rounded-xl transition-colors text-zinc-500 hover:bg-zinc-100 hover:text-zinc-700 dark:text-zinc-400 dark:hover:bg-slate-800 dark:hover:text-zinc-200 flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs("div", { className: "absolute top-full left-0 right-0 px-4 pt-2 pb-4 space-y-2 z-50", "data-section": "search-div-436", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs("div", { className: "rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", "data-section": "search-div-437", children: [_jsx("div", { className: "px-4 py-2 border-b border-zinc-200 dark:border-slate-700", "data-section": "search-div-438", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider font-semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
269
280
  const Icon = link.icon;
270
281
  const itemIndex = quickLinkItems.findIndex((item) => item.link.href === link.href);
271
282
  const isActive = activeIndex === itemIndex;
272
283
  return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
273
284
  onClose?.();
274
285
  router.push(link.href);
275
- }, onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 last:border-b-0 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}`, children: [_jsx(Icon, { className: "w-4 h-4 flex-shrink-0 text-zinc-500 dark:text-zinc-400" }), _jsx(Text, { size: "sm", className: "font-medium", children: link.label })] }) }, link.href));
276
- }) })] })), query && (_jsxs("div", { className: "rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", "data-section": "search-div-439", children: [suggestionsLoading ? (_jsx("div", { className: "px-4 py-3", "data-section": "search-div-440", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })) : suggestions.length > 0 ? (_jsx(Ul, { children: suggestions.map((suggestion, suggestionIndex) => {
277
- const itemIndex = quickLinkItems.length + suggestionIndex;
278
- const isActive = activeIndex === itemIndex;
279
- return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => handleSuggestionClick(suggestion), onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-3 px-4 py-3 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 last:border-b-0 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}`, children: [_jsx(Span, { className: "text-sm", children: typeIcons[suggestion.type] }), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "search-div-441", children: [_jsx(Text, { size: "sm", className: "font-medium truncate", children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] }), _jsx(Span, { className: `text-xs px-2 py-0.5 rounded-full ${typeBadges[suggestion.type]}`, children: suggestion.type })] }) }, suggestion.objectID));
280
- }) })) : null, _jsxs(Button, { type: "button", variant: "ghost", onClick: handleOverlaySearch, onMouseEnter: () => setActiveIndex(overlayItems.length - 1), className: `w-full flex items-center gap-2 px-4 py-3 text-left transition-colors ${activeIndex === overlayItems.length - 1 ? "bg-zinc-100 dark:bg-slate-800" : "hover:bg-zinc-50 dark:hover:bg-slate-800/70"}${suggestions.length > 0 ? " border-t border-zinc-200 dark:border-slate-700" : ""}`, children: [_jsx("svg", { className: "w-4 h-4 flex-shrink-0 text-zinc-500 dark:text-zinc-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Text, { size: "sm", className: "font-medium", children: labels.browseProducts(query) })] })] }))] }))] }) }));
286
+ }, onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors border-b border-zinc-200 dark:border-slate-700 last:border-b-0 ${isActive ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}`, children: [_jsx(Icon, { className: CLS_ICON_ROW }), _jsx(Text, { size: "sm", className: "font-medium", children: link.label })] }) }, link.href));
287
+ }) })] })), query && (_jsxs("div", { className: "rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900", "data-section": "search-div-439", children: [suggestionsLoading ? (_jsx("div", { className: "px-4 py-3", "data-section": "search-div-440", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })) : suggestions.length > 0 ? (_jsx(Ul, { children: suggestions.map((suggestion, suggestionIndex) => (_jsx(SuggestionItem, { suggestion: suggestion, itemIndex: getOverlaySuggestionIndex(suggestionIndex), activeIndex: activeIndex, onSelect: handleSuggestionClick, onHover: setActiveIndex, typeIcons: typeIcons, typeBadges: typeBadges }, suggestion.objectID))) })) : null, _jsxs(Button, { type: "button", variant: "ghost", onClick: handleOverlaySearch, onMouseEnter: () => setActiveIndex(overlayItems.length - 1), className: `w-full flex items-center gap-2 px-4 py-3 text-left transition-colors ${activeIndex === overlayItems.length - 1 ? "bg-zinc-100 dark:bg-slate-800" : CLS_HOVER_ROW}${suggestions.length > 0 ? " border-t border-zinc-200 dark:border-slate-700" : ""}`, children: [_jsx("svg", { className: CLS_ICON_ROW, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Text, { size: "sm", className: "font-medium", children: labels.browseProducts(query) })] })] }))] }))] }) }));
281
288
  }
@@ -26,10 +26,10 @@ export declare const searchProductItemSchema: z.ZodObject<{
26
26
  slug: string;
27
27
  currency?: string | undefined;
28
28
  status?: string | undefined;
29
- price?: number | undefined;
30
29
  featured?: boolean | undefined;
31
- listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
30
+ listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | undefined;
32
31
  isPromoted?: boolean | undefined;
32
+ price?: number | undefined;
33
33
  mainImage?: string | undefined;
34
34
  }, {
35
35
  id: string;
@@ -37,10 +37,10 @@ export declare const searchProductItemSchema: z.ZodObject<{
37
37
  slug: string;
38
38
  currency?: string | undefined;
39
39
  status?: string | undefined;
40
- price?: number | undefined;
41
40
  featured?: boolean | undefined;
42
- listingType?: "auction" | "standard" | "pre-order" | "prize-draw" | "bundle" | undefined;
41
+ listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | undefined;
43
42
  isPromoted?: boolean | undefined;
43
+ price?: number | undefined;
44
44
  mainImage?: string | undefined;
45
45
  }>;
46
46
  /**
@@ -63,8 +63,8 @@ export declare const searchQuerySchema: z.ZodObject<{
63
63
  }, {
64
64
  q: string;
65
65
  sort?: string | undefined;
66
- page?: number | undefined;
67
66
  category?: string | undefined;
67
+ page?: number | undefined;
68
68
  minPrice?: number | undefined;
69
69
  maxPrice?: number | undefined;
70
70
  }>;