@mohasinac/appkit 4.2.0 → 4.3.1

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 (258) hide show
  1. package/dist/_internal/client/features/layout/navActive.d.ts +17 -0
  2. package/dist/_internal/client/features/layout/navActive.js +33 -0
  3. package/dist/_internal/server/features/auth/actions.d.ts +0 -1
  4. package/dist/_internal/server/features/auth/actions.js +0 -1
  5. package/dist/_internal/server/features/bundles/data.d.ts +22 -4
  6. package/dist/_internal/server/features/bundles/data.js +41 -4
  7. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  8. package/dist/_internal/server/features/bundles/index.js +1 -1
  9. package/dist/_internal/server/features/checkout/actions.js +7 -78
  10. package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -16
  11. package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -30
  12. package/dist/_internal/server/features/orders/adapters.js +1 -14
  13. package/dist/_internal/server/features/products/actions.js +8 -3
  14. package/dist/_internal/server/features/products/service.d.ts +15 -0
  15. package/dist/_internal/server/features/products/service.js +41 -0
  16. package/dist/_internal/server/functions/firestore.d.ts +3 -1
  17. package/dist/_internal/server/functions/firestore.js +17 -1
  18. package/dist/_internal/server/functions/scheduled.d.ts +2 -5
  19. package/dist/_internal/server/functions/scheduled.js +6 -30
  20. package/dist/_internal/server/jobs/core/bundleStockSync.js +25 -7
  21. package/dist/_internal/server/jobs/core/countersReconcile.js +7 -1
  22. package/dist/_internal/server/jobs/core/index.d.ts +4 -4
  23. package/dist/_internal/server/jobs/core/index.js +4 -4
  24. package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.d.ts +19 -0
  25. package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.js +30 -0
  26. package/dist/_internal/server/jobs/core/paymentWindowTimeout.d.ts +2 -2
  27. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +2 -2
  28. package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.d.ts +20 -0
  29. package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +113 -0
  30. package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.d.ts +14 -0
  31. package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +74 -0
  32. package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.d.ts +22 -0
  33. package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +68 -0
  34. package/dist/_internal/server/jobs/handlers/index.d.ts +3 -4
  35. package/dist/_internal/server/jobs/handlers/index.js +8 -6
  36. package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.d.ts +3 -0
  37. package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.js +11 -0
  38. package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.d.ts +2 -0
  39. package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.js +2 -0
  40. package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.d.ts +3 -0
  41. package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.js +4 -0
  42. package/dist/_internal/shared/actions/action-registry.js +40 -10
  43. package/dist/_internal/shared/checkout/rules/_defaults.js +1 -2
  44. package/dist/_internal/shared/checkout/rules/_limits.d.ts +3 -0
  45. package/dist/_internal/shared/checkout/rules/_limits.js +3 -0
  46. package/dist/_internal/shared/checkout/rules/_registry.d.ts +2 -1
  47. package/dist/_internal/shared/checkout/rules/_registry.js +2 -2
  48. package/dist/_internal/shared/checkout/rules/classified.rule.js +0 -1
  49. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  50. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  51. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +20 -1
  52. package/dist/_internal/shared/checkout/rules/types.d.ts +17 -7
  53. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +2 -0
  54. package/dist/_internal/shared/features/categories/bundle-copy.js +2 -0
  55. package/dist/_internal/shared/features/categories/bundle-pricing.d.ts +23 -0
  56. package/dist/_internal/shared/features/categories/bundle-pricing.js +25 -0
  57. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  58. package/dist/constants/api-endpoints.d.ts +6 -9
  59. package/dist/constants/api-endpoints.js +5 -5
  60. package/dist/constants/field-names.d.ts +17 -1
  61. package/dist/constants/field-names.js +17 -1
  62. package/dist/features/account/components/UserSidebar.js +21 -9
  63. package/dist/features/account/hooks/useNotifications.js +5 -0
  64. package/dist/features/admin/actions/admin-actions.js +6 -0
  65. package/dist/features/admin/actions/notification-actions.js +1 -2
  66. package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
  67. package/dist/features/admin/components/AdminGuideHubView.js +15 -1
  68. package/dist/features/admin/components/AdminPaymentsGuideView.d.ts +2 -0
  69. package/dist/features/admin/components/AdminPaymentsGuideView.js +36 -0
  70. package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
  71. package/dist/features/admin/components/AdminSidebar.js +18 -9
  72. package/dist/features/admin/components/AdminSiteSettingsView.js +33 -6
  73. package/dist/features/admin/components/AdminTesterChecklistView.js +74 -22
  74. package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +2 -0
  75. package/dist/features/admin/components/AdminWhatsAppGuideView.js +35 -0
  76. package/dist/features/admin/hooks/useAdminListing.d.ts +4 -0
  77. package/dist/features/admin/hooks/useAdminListing.js +4 -2
  78. package/dist/features/admin/schemas/firestore.d.ts +13 -5
  79. package/dist/features/admin/schemas/firestore.js +1 -2
  80. package/dist/features/auctions/actions/bid-actions.js +3 -1
  81. package/dist/features/auctions/components/AuctionBidsTable.d.ts +6 -1
  82. package/dist/features/auctions/components/AuctionBidsTable.js +8 -4
  83. package/dist/features/auctions/components/AuctionDetailPageView.js +10 -13
  84. package/dist/features/auctions/components/CollapsibleBidHistory.d.ts +23 -2
  85. package/dist/features/auctions/components/CollapsibleBidHistory.js +22 -3
  86. package/dist/features/auctions/components/LiveBidPrice.d.ts +19 -0
  87. package/dist/features/auctions/components/LiveBidPrice.js +17 -0
  88. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
  89. package/dist/features/auctions/components/PlaceBidFormClient.js +16 -1
  90. package/dist/features/auctions/hooks/useBids.d.ts +33 -0
  91. package/dist/features/auctions/hooks/useBids.js +32 -0
  92. package/dist/features/auctions/hooks/useLiveAuctionBid.d.ts +20 -0
  93. package/dist/features/auctions/hooks/useLiveAuctionBid.js +23 -0
  94. package/dist/features/auth/checkout-value-otp.d.ts +7 -10
  95. package/dist/features/auth/checkout-value-otp.js +14 -10
  96. package/dist/features/auth/server.d.ts +0 -1
  97. package/dist/features/auth/server.js +0 -1
  98. package/dist/features/blog/api/[slug]/route.d.ts +5 -0
  99. package/dist/features/blog/api/[slug]/route.js +12 -3
  100. package/dist/features/blog/components/BlogPostView.d.ts +2 -0
  101. package/dist/features/blog/components/BlogPostView.js +5 -4
  102. package/dist/features/blog/hooks/useBlog.d.ts +2 -0
  103. package/dist/features/blog/hooks/useBlog.js +2 -0
  104. package/dist/features/blog/repository/blog.repository.d.ts +7 -0
  105. package/dist/features/blog/repository/blog.repository.js +44 -0
  106. package/dist/features/blog/schemas/firestore.d.ts +3 -0
  107. package/dist/features/blog/schemas/firestore.js +1 -0
  108. package/dist/features/blog/types/index.d.ts +2 -0
  109. package/dist/features/cart/components/index.d.ts +0 -2
  110. package/dist/features/cart/components/index.js +0 -1
  111. package/dist/features/cart/hooks/useCartCount.js +22 -5
  112. package/dist/features/cart/hooks/useGuestCart.js +11 -1
  113. package/dist/features/cart/utils/guest-cart.d.ts +4 -0
  114. package/dist/features/cart/utils/guest-cart.js +10 -0
  115. package/dist/features/cart/utils/pending-ops.d.ts +17 -0
  116. package/dist/features/cart/utils/pending-ops.js +46 -5
  117. package/dist/features/categories/components/BrandDetailPageView.js +6 -2
  118. package/dist/features/categories/components/BundleDetailView.js +4 -2
  119. package/dist/features/categories/components/CategoryDetailPageView.js +10 -2
  120. package/dist/features/categories/schemas/firestore.d.ts +7 -0
  121. package/dist/features/checkout/actions/index.d.ts +0 -1
  122. package/dist/features/checkout/actions/index.js +0 -1
  123. package/dist/features/checkout/schemas/firestore.d.ts +2 -2
  124. package/dist/features/events/actions/event-actions.d.ts +2 -0
  125. package/dist/features/events/actions/event-actions.js +5 -0
  126. package/dist/features/events/components/EventCard.js +2 -3
  127. package/dist/features/events/components/RelatedEventsCarousel.d.ts +15 -0
  128. package/dist/features/events/components/RelatedEventsCarousel.js +13 -0
  129. package/dist/features/events/components/index.d.ts +2 -0
  130. package/dist/features/events/components/index.js +1 -0
  131. package/dist/features/events/repository/events.repository.d.ts +6 -0
  132. package/dist/features/events/repository/events.repository.js +21 -0
  133. package/dist/features/history/repository/user-history.repository.d.ts +1 -1
  134. package/dist/features/history/utils/guest-history.d.ts +1 -1
  135. package/dist/features/homepage/components/FeaturedBundlesSection.js +4 -2
  136. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  137. package/dist/features/homepage/components/SectionCarousel.js +1 -1
  138. package/dist/features/homepage/components/WelcomeSection.js +1 -1
  139. package/dist/features/layout/AppLayoutShell.js +44 -10
  140. package/dist/features/layout/NavbarLayout.js +1 -1
  141. package/dist/features/layout/TitleBarLayout.js +2 -2
  142. package/dist/features/media/MediaVideo.d.ts +7 -0
  143. package/dist/features/media/MediaVideo.js +36 -5
  144. package/dist/features/orders/components/OrdersList.js +1 -5
  145. package/dist/features/orders/schemas/firestore.d.ts +2 -8
  146. package/dist/features/orders/schemas/index.d.ts +3 -32
  147. package/dist/features/orders/schemas/index.js +1 -4
  148. package/dist/features/orders/types/index.d.ts +6 -9
  149. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  150. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
  151. package/dist/features/products/actions/product-actions.js +13 -10
  152. package/dist/features/products/components/MarketplaceBundleCard.d.ts +1 -1
  153. package/dist/features/products/components/MarketplaceBundleCard.js +7 -4
  154. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  155. package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
  156. package/dist/features/products/components/PrizeDrawWinnerMappingView.d.ts +14 -0
  157. package/dist/features/products/components/PrizeDrawWinnerMappingView.js +37 -0
  158. package/dist/features/products/components/PrizeRevealModal.d.ts +10 -23
  159. package/dist/features/products/components/PrizeRevealModal.js +18 -147
  160. package/dist/features/products/components/ProductDetailPageView.js +72 -30
  161. package/dist/features/products/components/ProductForm.js +7 -14
  162. package/dist/features/products/components/ProductGalleryClient.d.ts +10 -1
  163. package/dist/features/products/components/ProductGalleryClient.js +29 -10
  164. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  165. package/dist/features/products/components/RelatedProductsCarousel.js +2 -2
  166. package/dist/features/products/components/ShowGroupSection.js +2 -2
  167. package/dist/features/products/components/SublistingCarouselSection.js +1 -1
  168. package/dist/features/products/components/index.d.ts +4 -2
  169. package/dist/features/products/components/index.js +1 -0
  170. package/dist/features/products/constants/action-defs.d.ts +1 -3
  171. package/dist/features/products/constants/action-defs.js +0 -5
  172. package/dist/features/products/repository/products.repository.d.ts +6 -0
  173. package/dist/features/products/repository/products.repository.js +20 -1
  174. package/dist/features/products/schemas/firestore.d.ts +28 -3
  175. package/dist/features/products/schemas/index.d.ts +11 -3
  176. package/dist/features/products/schemas/index.js +10 -2
  177. package/dist/features/products/types/index.d.ts +2 -1
  178. package/dist/features/reviews/actions/review-actions.js +1 -0
  179. package/dist/features/reviews/components/ReviewDetailPageView.js +8 -1
  180. package/dist/features/reviews/components/ReviewDetailShell.js +2 -1
  181. package/dist/features/reviews/components/ReviewsList.js +2 -1
  182. package/dist/features/reviews/schemas/firestore.d.ts +3 -0
  183. package/dist/features/reviews/schemas/index.d.ts +6 -0
  184. package/dist/features/reviews/schemas/index.js +2 -0
  185. package/dist/features/reviews/types/index.d.ts +3 -0
  186. package/dist/features/scams/actions/scam-actions.d.ts +3 -0
  187. package/dist/features/scams/actions/scam-actions.js +3 -2
  188. package/dist/features/scams/components/ScamProfileView.d.ts +2 -1
  189. package/dist/features/scams/components/ScamProfileView.js +2 -2
  190. package/dist/features/scams/repository/scammer.repository.d.ts +6 -0
  191. package/dist/features/scams/repository/scammer.repository.js +24 -0
  192. package/dist/features/seller/actions/seller-actions.js +8 -0
  193. package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -0
  194. package/dist/features/seller/components/SellerSidebar.js +20 -9
  195. package/dist/features/stores/components/StoreDetailLayoutView.js +6 -6
  196. package/dist/features/stores/components/StoreGuideHubView.js +7 -1
  197. package/dist/features/stores/components/StoreNavTabs.d.ts +10 -1
  198. package/dist/features/stores/components/StoreNavTabs.js +22 -9
  199. package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +3 -0
  200. package/dist/features/stores/components/StoreWhatsAppGuideView.js +12 -0
  201. package/dist/features/stores/components/index.d.ts +2 -0
  202. package/dist/features/stores/components/index.js +1 -0
  203. package/dist/features/tester/actions/index.d.ts +1 -0
  204. package/dist/features/tester/actions/index.js +1 -0
  205. package/dist/features/tester/actions/leaderboard-actions.d.ts +3 -0
  206. package/dist/features/tester/actions/leaderboard-actions.js +5 -0
  207. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +26 -4
  208. package/dist/features/tester/components/AdminTesterFeedbackListView.js +13 -0
  209. package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +23 -0
  210. package/dist/features/tester/components/BugHunterLeaderboardView.js +18 -0
  211. package/dist/features/tester/components/index.d.ts +2 -0
  212. package/dist/features/tester/components/index.js +1 -0
  213. package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +12 -1
  214. package/dist/features/tester/repository/tester-checklist-item.repository.js +80 -0
  215. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +4 -0
  216. package/dist/features/tester/repository/tester-checklist-response.repository.js +4 -0
  217. package/dist/features/tester/schemas/firestore.d.ts +22 -1
  218. package/dist/features/tester/schemas/firestore.js +8 -0
  219. package/dist/features/tester/seed-data/categories-tester-seed-data.js +42 -7
  220. package/dist/features/tester/seed-data/events-tester-seed-data.js +23 -0
  221. package/dist/features/tester/seed-data/orders-tester-seed-data.js +69 -31
  222. package/dist/features/tester/seed-data/products-tester-seed-data.js +154 -9
  223. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +252 -15
  224. package/dist/features/wishlist/hooks/useGuestWishlist.js +11 -1
  225. package/dist/features/wishlist/hooks/useWishlistCount.js +16 -2
  226. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -0
  227. package/dist/features/wishlist/utils/guest-wishlist.js +11 -0
  228. package/dist/index.d.ts +23 -22
  229. package/dist/index.js +31 -58
  230. package/dist/jobs.d.ts +1 -1
  231. package/dist/jobs.js +2 -2
  232. package/dist/next/routing/route-map.d.ts +9 -2
  233. package/dist/next/routing/route-map.js +4 -1
  234. package/dist/schemas/registry.d.ts +6 -26
  235. package/dist/seed/bids-seed-data.js +14 -2
  236. package/dist/seed/blog-posts-seed-data.js +2 -1
  237. package/dist/seed/categories-seed-data.js +11 -2
  238. package/dist/seed/events-seed-data.js +11 -11
  239. package/dist/seed/faq-seed-data.js +5 -1
  240. package/dist/seed/products-auctions-seed-data.js +2 -2
  241. package/dist/seed/products-preorders-seed-data.d.ts +2 -1
  242. package/dist/seed/products-prize-draws-seed-data.js +56 -10
  243. package/dist/seed/products-standard-seed-data.js +80 -3
  244. package/dist/seed/scammers-seed-data.js +75 -2
  245. package/dist/seed/site-settings-seed-data.js +5 -2
  246. package/dist/server-entry.d.ts +0 -1
  247. package/dist/server-entry.js +0 -1
  248. package/dist/server.d.ts +4 -20
  249. package/dist/server.js +10 -57
  250. package/dist/styles.css +1 -277
  251. package/dist/tailwind-utilities.css +1 -1
  252. package/dist/ui/components/BaseListingCard.js +1 -1
  253. package/dist/ui/components/ImageLightbox.d.ts +9 -0
  254. package/dist/ui/components/ImageLightbox.js +6 -3
  255. package/dist/ui/components/ListingToolbar.d.ts +18 -1
  256. package/dist/ui/components/ListingToolbar.js +5 -2
  257. package/dist/ui/components/StickyToolbar.js +13 -8
  258. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
+ import { Play } from "lucide-react";
4
5
  import { ImageLightbox } from "../../../ui/components/ImageLightbox";
5
6
  import { Button, Div, Row, Stack, Text } from "../../../ui";
6
7
  import { MediaImage } from "../../media/MediaImage";
@@ -9,23 +10,41 @@ const __O = {
9
10
  xAuto: "overflow-x-auto",
10
11
  };
11
12
  const PlaceholderSvg = () => (_jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), _jsx("path", { d: "M21 15l-5-5L5 21" })] }));
12
- export function ProductGalleryClient({ images, productName }) {
13
+ export function ProductGalleryClient({ images, video, productName }) {
13
14
  const [activeIndex, setActiveIndex] = useState(0);
14
15
  const [lightboxIndex, setLightboxIndex] = useState(null);
15
- const lightboxImages = images.map((src, i) => ({
16
- src,
17
- alt: productName ? `${productName} image ${i + 1}` : `Product image ${i + 1}`,
16
+ const slides = [
17
+ ...images.map((src) => ({ kind: "image", src })),
18
+ ...(video ? [{ kind: "video", src: video.url, poster: video.thumbnailUrl }] : []),
19
+ ];
20
+ const lightboxImages = slides.map((slide, i) => ({
21
+ src: slide.src,
22
+ alt: productName
23
+ ? `${productName} — ${slide.kind === "video" ? "video" : `image ${i + 1}`}`
24
+ : slide.kind === "video"
25
+ ? "Product video"
26
+ : `Product image ${i + 1}`,
27
+ kind: slide.kind,
28
+ poster: slide.kind === "video" ? slide.poster : undefined,
18
29
  }));
19
- if (images.length === 0) {
30
+ if (slides.length === 0) {
20
31
  return (_jsx(Div, { className: `${__O.hidden}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsx(Row, { className: "aspect-square text-[var(--appkit-color-border)] dark:text-[var(--appkit-color-border-subtle)]", align: "center", justify: "center", children: _jsx(PlaceholderSvg, {}) }) }));
21
32
  }
22
- const mainImage = images[activeIndex] ?? images[0];
23
- return (_jsxs(Stack, { gap: "3", children: [_jsx(Div, { rounded: "xl", className: "overflow-hidden border border-[var(--appkit-color-border-subtle)] cursor-zoom-in aspect-square w-full", onClick: () => setLightboxIndex(activeIndex), role: "button", tabIndex: 0, onKeyDown: (e) => {
33
+ const activeSlide = slides[activeIndex] ?? slides[0];
34
+ return (_jsxs(Stack, { gap: "3", children: [_jsxs(Div, { rounded: "xl", className: "overflow-hidden border border-[var(--appkit-color-border-subtle)] cursor-zoom-in aspect-square w-full relative", onClick: () => setLightboxIndex(activeIndex), role: "button", tabIndex: 0, onKeyDown: (e) => {
24
35
  if (e.key === "Enter" || e.key === " ")
25
36
  setLightboxIndex(activeIndex);
26
- }, "aria-label": `View ${productName ?? "product"} image in full screen`, children: _jsx(MediaImage, { src: mainImage, alt: productName ? `${productName} — image ${activeIndex + 1}` : `Product image ${activeIndex + 1}`, size: "gallery", className: "transition-transform duration-300 hover:scale-105" }) }), images.length > 1 && (_jsx(Div, { layout: "flex", gap: "2", className: `${__O.xAuto}`, padding: "b-2xs", children: images.map((src, i) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setActiveIndex(i), rounded: "lg", paddingX: "none", paddingY: "none", className: `flex-shrink-0 h-16 w-16 overflow-hidden border-2 transition-all${i === activeIndex
37
+ }, "aria-label": activeSlide.kind === "video"
38
+ ? `Play ${productName ?? "product"} video`
39
+ : `View ${productName ?? "product"} image in full screen`, children: [_jsx(MediaImage, { src: activeSlide.kind === "video" ? activeSlide.poster : activeSlide.src, alt: activeSlide.kind === "video"
40
+ ? productName
41
+ ? `${productName} — video`
42
+ : "Product video"
43
+ : productName
44
+ ? `${productName} — image ${activeIndex + 1}`
45
+ : `Product image ${activeIndex + 1}`, size: "gallery", className: "transition-transform duration-300 hover:scale-105" }), activeSlide.kind === "video" && (_jsx(Row, { className: "absolute inset-0 pointer-events-none", align: "center", justify: "center", children: _jsx(Row, { surface: "overlay-sm", rounded: "full", className: "h-16 w-16 text-white", align: "center", justify: "center", children: _jsx(Play, { className: "h-7 w-7 fill-current", "aria-hidden": "true" }) }) }))] }), slides.length > 1 && (_jsx(Div, { layout: "flex", gap: "2", className: `${__O.xAuto}`, padding: "b-2xs", children: slides.map((slide, i) => (_jsxs(Button, { variant: "ghost", type: "button", onClick: () => setActiveIndex(i), rounded: "lg", paddingX: "none", paddingY: "none", className: `relative flex-shrink-0 h-16 w-16 overflow-hidden border-2 transition-all${i === activeIndex
27
46
  ? " border-primary-500"
28
- : " border-transparent opacity-60 hover:opacity-100"}`, "aria-label": `View image ${i + 1}`, "aria-current": i === activeIndex ? "true" : undefined, children: _jsx(MediaImage, { src: src, alt: `Thumbnail ${i + 1}`, size: "thumbnail" }) }, i))) })), images.length > 1 && (
47
+ : " border-transparent opacity-60 hover:opacity-100"}`, "aria-label": slide.kind === "video" ? "View video" : `View image ${i + 1}`, "aria-current": i === activeIndex ? "true" : undefined, children: [_jsx(MediaImage, { src: slide.kind === "video" ? slide.poster : slide.src, alt: slide.kind === "video" ? "Video thumbnail" : `Thumbnail ${i + 1}`, size: "thumbnail" }), slide.kind === "video" && (_jsx(Row, { surface: "overlay-xs", className: "absolute inset-0 pointer-events-none", align: "center", justify: "center", children: _jsx(Play, { className: "h-5 w-5 text-white fill-current", "aria-hidden": "true" }) }))] }, i))) })), slides.length > 1 && (
29
48
  // audit-content-alignment-ok: functional image-counter label under the gallery, not page content
30
- _jsxs(Text, { size: "xs", align: "center", color: "faint", children: [activeIndex + 1, " / ", images.length] })), _jsx(ImageLightbox, { images: lightboxImages, activeIndex: lightboxIndex, onClose: () => setLightboxIndex(null), onNavigate: (i) => setLightboxIndex(i) })] }));
49
+ _jsxs(Text, { size: "xs", align: "center", color: "faint", children: [activeIndex + 1, " / ", slides.length] })), _jsx(ImageLightbox, { images: lightboxImages, activeIndex: lightboxIndex, onClose: () => setLightboxIndex(null), onNavigate: (i) => setLightboxIndex(i) })] }));
31
50
  }
@@ -11,6 +11,7 @@ import { useAuthGate } from "../../../react/hooks/useAuthGate";
11
11
  import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../constants/action-defs";
12
12
  import { CompareOverlay } from "./CompareOverlay";
13
13
  import { ROUTES } from "../../../next";
14
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
14
15
  import { ProductGrid, ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from ".";
15
16
  import { useGuestCart } from "../../cart/hooks/useGuestCart";
16
17
  import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
@@ -241,7 +242,7 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
241
242
  setCompareIds(ids);
242
243
  },
243
244
  },
244
- ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
245
+ ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => pluginFor(p.listingType ?? "standard").detailRoute(p.slug || p.id), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
245
246
  setCompareIds([]);
246
247
  selection.clearSelection();
247
248
  }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions, featureOptions: featureOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { ROUTES } from "../../../next";
3
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
4
4
  import { SectionCarousel } from "../../homepage/components/SectionCarousel";
5
5
  import { ProductCard } from "./ProductGrid";
6
6
  import { CAROUSEL_PER_VIEW } from "../../homepage/constants/carousel-per-view";
7
7
  export function RelatedProductsCarousel({ items, title = "You might also like", }) {
8
8
  if (items.length === 0)
9
9
  return null;
10
- return (_jsx(SectionCarousel, { title: title, pillLabel: "Related Products", headingVariant: "editorial", items: items, isLoading: false, perView: CAROUSEL_PER_VIEW.standard, keyExtractor: (item) => item.id, renderItem: (item) => (_jsx(ProductCard, { product: item, href: item.slug || item.id ? String(ROUTES.PUBLIC.PRODUCT_DETAIL(item.slug || item.id)) : undefined })) }));
10
+ return (_jsx(SectionCarousel, { title: title, pillLabel: "Related Products", headingVariant: "editorial", items: items, isLoading: false, perView: CAROUSEL_PER_VIEW.standard, keyExtractor: (item) => item.id, renderItem: (item) => (_jsx(ProductCard, { product: item, href: item.slug || item.id ? pluginFor(item.listingType ?? "standard").detailRoute(item.slug || item.id) : undefined })) }));
11
11
  }
@@ -28,7 +28,7 @@ function GroupTableRow({ member }) {
28
28
  const href = memberHref(member);
29
29
  const price = formatCurrency(member.price, member.currency ?? "INR");
30
30
  const image = member.images?.[0] ?? "";
31
- return (_jsxs(Tr, { className: "last:border-0", border: "subtle", children: [_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Div, { className: `w-10 h-10 ${__O.hidden}`, rounded: "full", border: "default", children: _jsx(MediaImage, { src: image, alt: member.title, size: "thumbnail" }) }) }), _jsxs(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: [_jsx(Text, { className: "line-clamp-2", color: "primary", size: "sm", weight: "medium", children: member.title }), member.isGroupParent && (_jsx(Span, { weight: "semibold", className: "text-[10px] text-[var(--appkit-color-primary)]", children: "Parent" }))] }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "sm", color: "muted", children: price }) }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "xs", transform: "capitalize", color: "muted", children: member.condition ?? "—" }) }), _jsx(Td, { padding: "xs-tall", children: _jsx(Link, { href: href, className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline", children: "View \u00E2\u2020\u2019" }) })] }));
31
+ return (_jsxs(Tr, { className: "last:border-0", border: "subtle", children: [_jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Div, { className: `w-10 h-10 ${__O.hidden}`, rounded: "full", border: "default", children: _jsx(MediaImage, { src: image, alt: member.title, size: "thumbnail" }) }) }), _jsxs(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: [_jsx(Text, { className: "line-clamp-2", color: "primary", size: "sm", weight: "medium", children: member.title }), member.isGroupParent && (_jsx(Span, { weight: "semibold", className: "text-[10px] text-[var(--appkit-color-primary)]", children: "Parent" }))] }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "sm", color: "muted", children: price }) }), _jsx(Td, { paddingSide: "pr-sm", padding: "xs-tall", children: _jsx(Text, { size: "xs", transform: "capitalize", color: "muted", children: member.condition ?? "—" }) }), _jsx(Td, { padding: "xs-tall", children: _jsx(Link, { href: href, className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline", children: "View \u2192" }) })] }));
32
32
  }
33
33
  export function ShowGroupSection({ groupId, currentSlug, isParent, groupTitle }) {
34
34
  const [open, setOpen] = useState(false);
@@ -54,7 +54,7 @@ export function ShowGroupSection({ groupId, currentSlug, isParent, groupTitle })
54
54
  return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: `${__O.hidden}`, surface: "muted", rounded: "xl", border: "default", children: [_jsxs(Row, { role: "button", tabIndex: 0, onClick: () => setOpen((v) => !v), onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") {
55
55
  e.preventDefault();
56
56
  setOpen((v) => !v);
57
- } }, justify: "between", paddingX: "x-md", paddingY: "y-sm", className: "w-full cursor-pointer text-left hover:bg-[var(--appkit-color-surface-elevated)]/70 transition-colors", "aria-expanded": open, children: [_jsxs(Row, { align: "center", gap: "xs", children: [_jsx(Span, { size: "xs", className: "mr-1", color: "faint", children: open ? "â–¼" : "â–¶" }), _jsx(Text, { size: "sm", weight: "medium", color: "primary", children: parentLabel }), _jsx(Span, { size: "xs", className: "ml-1", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: members.length })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); setShowAll(true); }, className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline ml-3 flex-shrink-0", children: "View whole group \u00E2\u2020\u2019" })] }), open && (_jsx(Div, { className: `pb-[1rem] pt-[0.25rem] ${__O.xAuto}`, padding: "x-md", children: _jsx(Row, { gap: "3", className: "min-w-0", children: [...members]
57
+ } }, justify: "between", paddingX: "x-md", paddingY: "y-sm", className: "w-full cursor-pointer text-left hover:bg-[var(--appkit-color-surface-elevated)]/70 transition-colors", "aria-expanded": open, children: [_jsxs(Row, { align: "center", gap: "xs", children: [_jsx(Span, { size: "xs", className: "mr-1", color: "faint", children: open ? "" : "" }), _jsx(Text, { size: "sm", weight: "medium", color: "primary", children: parentLabel }), _jsx(Span, { size: "xs", className: "ml-1", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: members.length })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); setShowAll(true); }, className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline ml-3 flex-shrink-0", children: "View whole group \u2192" })] }), open && (_jsx(Div, { className: `pb-[1rem] pt-[0.25rem] ${__O.xAuto}`, padding: "x-md", children: _jsx(Row, { gap: "3", className: "min-w-0", children: [...members]
58
58
  .sort((a, b) => (b.isGroupParent ? 1 : 0) - (a.isGroupParent ? 1 : 0))
59
59
  .map((m) => (_jsx(MemberThumb, { member: m, isCurrent: m.slug === currentSlug || m.id === currentSlug }, m.id))) }) }))] }), useDrawer ? (_jsx(SideDrawer, { isOpen: showAll, onClose: () => setShowAll(false), title: label, children: tableContent })) : (_jsx(Modal, { open: showAll, onClose: () => setShowAll(false), title: label, children: tableContent }))] }));
60
60
  }
@@ -54,5 +54,5 @@ export function SublistingCarouselSection({ sublistingCategoryId, currentListing
54
54
  return (_jsxs(Div, { className: `${__O.hidden}`, surface: "muted", rounded: "xl", border: "default", children: [_jsxs(Row, { role: "button", tabIndex: 0, onClick: () => setOpen((v) => !v), onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") {
55
55
  e.preventDefault();
56
56
  setOpen((v) => !v);
57
- } }, justify: "between", paddingX: "x-md", paddingY: "y-sm", className: "w-full cursor-pointer text-left hover:bg-[var(--appkit-color-surface-elevated)]/70 transition-colors", "aria-expanded": open, children: [_jsxs(Row, { align: "center", gap: "xs", children: [_jsx(Span, { size: "xs", className: "mr-1", color: "faint", children: open ? "â–¼" : "â–¶" }), _jsxs(Text, { size: "sm", weight: "medium", color: "primary", children: ["More listings like this:", " ", _jsx(Span, { className: "text-[var(--appkit-color-primary)]", children: label })] }), _jsx(Span, { size: "xs", className: "ml-1", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: listings.length })] }), category && (_jsx(Link, { href: String(ROUTES.PUBLIC.SUBLISTING_CATEGORY(category.slug)), onClick: (e) => e.stopPropagation(), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline ml-3 flex-shrink-0", children: "View all \u00E2\u2020\u2019" }))] }), open && (_jsx(Div, { className: `pb-[1rem] pt-[0.25rem] ${__O.xAuto}`, padding: "x-md", children: _jsx(Row, { gap: "3", className: "min-w-0", children: listings.map((listing) => (_jsx(ListingThumb, { listing: listing, isCurrent: listing.id === currentListingId }, listing.id))) }) }))] }));
57
+ } }, justify: "between", paddingX: "x-md", paddingY: "y-sm", className: "w-full cursor-pointer text-left hover:bg-[var(--appkit-color-surface-elevated)]/70 transition-colors", "aria-expanded": open, children: [_jsxs(Row, { align: "center", gap: "xs", children: [_jsx(Span, { size: "xs", className: "mr-1", color: "faint", children: open ? "â–¼" : "â–¶" }), _jsxs(Text, { size: "sm", weight: "medium", color: "primary", children: ["More listings like this:", " ", _jsx(Span, { className: "text-[var(--appkit-color-primary)]", children: label })] }), _jsx(Span, { size: "xs", className: "ml-1", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: listings.length })] }), category && (_jsx(Link, { href: String(ROUTES.PUBLIC.SUBLISTING_CATEGORY(category.slug)), onClick: (e) => e.stopPropagation(), className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-primary)] hover:underline ml-3 flex-shrink-0", children: "View all \u2192" }))] }), open && (_jsx(Div, { className: `pb-[1rem] pt-[0.25rem] ${__O.xAuto}`, padding: "x-md", children: _jsx(Row, { gap: "3", className: "min-w-0", children: listings.map((listing) => (_jsx(ListingThumb, { listing: listing, isCurrent: listing.id === currentListingId }, listing.id))) }) }))] }));
58
58
  }
@@ -12,7 +12,9 @@ export type { PrizeDrawItemsEditorProps } from "./PrizeDrawItemsEditor";
12
12
  export { PrizeDrawCollage } from "./PrizeDrawCollage";
13
13
  export type { PrizeDrawCollageProps } from "./PrizeDrawCollage";
14
14
  export { PrizeRevealModal } from "./PrizeRevealModal";
15
- export type { PrizeRevealModalProps, PrizeRevealResponse, } from "./PrizeRevealModal";
15
+ export type { PrizeRevealModalProps, PrizeRevealResult, } from "./PrizeRevealModal";
16
+ export { PrizeDrawWinnerMappingView } from "./PrizeDrawWinnerMappingView";
17
+ export type { PrizeDrawWinnerMappingViewProps, PrizeDrawWinnerMappingRow, } from "./PrizeDrawWinnerMappingView";
16
18
  export { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
17
19
  export type { MarketplacePrizeDrawCardProps, MarketplacePrizeDrawCardData, MarketplacePrizeDrawCardLabels, } from "./MarketplacePrizeDrawCard";
18
20
  export { MarketplaceBundleCard } from "./MarketplaceBundleCard";
@@ -51,7 +53,7 @@ export type { MakeOfferButtonProps } from "./MakeOfferButton";
51
53
  export { RelatedProducts } from "./RelatedProducts";
52
54
  export type { RelatedProductsProps } from "./RelatedProducts";
53
55
  export { ProductGalleryClient } from "./ProductGalleryClient";
54
- export type { ProductGalleryClientProps } from "./ProductGalleryClient";
56
+ export type { ProductGalleryClientProps, ProductGalleryVideo } from "./ProductGalleryClient";
55
57
  export { ProductTabsShell } from "./ProductTabsShell";
56
58
  export type { ProductTabsShellProps, CustomTabDef } from "./ProductTabsShell";
57
59
  export { ProductGradingTab } from "./ProductGradingTab";
@@ -6,6 +6,7 @@ export { NonRefundableConsentModal } from "./NonRefundableConsentModal";
6
6
  export { PrizeDrawItemsEditor } from "./PrizeDrawItemsEditor";
7
7
  export { PrizeDrawCollage } from "./PrizeDrawCollage";
8
8
  export { PrizeRevealModal } from "./PrizeRevealModal";
9
+ export { PrizeDrawWinnerMappingView } from "./PrizeDrawWinnerMappingView";
9
10
  export { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
10
11
  export { MarketplaceBundleCard } from "./MarketplaceBundleCard";
11
12
  export { PrizeDrawsIndexListing } from "./PrizeDrawsIndexListing";
@@ -32,7 +32,6 @@ export declare const ACTION_ID: {
32
32
  readonly CANCEL_RESERVATION: "cancel-reservation";
33
33
  readonly CHECKOUT: "checkout";
34
34
  readonly CONTINUE_TO_VERIFY: "continue-to-verification";
35
- readonly SEND_OTP: "send-otp";
36
35
  readonly VERIFY_OTP: "verify-otp";
37
36
  readonly PAY_ONLINE: "pay-online";
38
37
  readonly PAY_COD: "pay-cod";
@@ -287,7 +286,6 @@ export declare const DASHBOARD_QUICK_ACTION_ID: {
287
286
  readonly ADMIN_EVENTS: "dqa-admin-events";
288
287
  readonly ADMIN_SUPPORT: "dqa-admin-support";
289
288
  readonly ADMIN_MODERATION: "dqa-admin-moderation";
290
- readonly ADMIN_INTEGRATION_GUIDES: "dqa-admin-integration-guides";
291
289
  readonly ADMIN_USERS: "dqa-admin-users";
292
290
  readonly ADMIN_CATEGORIES: "dqa-admin-categories";
293
291
  readonly ADMIN_REVIEWS: "dqa-admin-reviews";
@@ -334,7 +332,7 @@ export interface DashboardQuickActionMeta {
334
332
  }
335
333
  export declare const DASHBOARD_QUICK_ACTION_META: Record<DashboardQuickActionId, DashboardQuickActionMeta>;
336
334
  export declare const DASHBOARD_QUICK_ACTIONS: {
337
- readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-user", "dqa-admin-add-store", "dqa-admin-add-coupon", "dqa-admin-add-event", "dqa-admin-add-blog", "dqa-admin-users", "dqa-admin-stores", "dqa-admin-orders", "dqa-admin-payouts", "dqa-admin-analytics", "dqa-admin-categories", "dqa-admin-reviews", "dqa-admin-events", "dqa-admin-faqs", "dqa-admin-carousel", "dqa-admin-sections", "dqa-admin-support", "dqa-admin-moderation", "dqa-admin-integration-guides", "dqa-admin-settings"];
335
+ readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-user", "dqa-admin-add-store", "dqa-admin-add-coupon", "dqa-admin-add-event", "dqa-admin-add-blog", "dqa-admin-users", "dqa-admin-stores", "dqa-admin-orders", "dqa-admin-payouts", "dqa-admin-analytics", "dqa-admin-categories", "dqa-admin-reviews", "dqa-admin-events", "dqa-admin-faqs", "dqa-admin-carousel", "dqa-admin-sections", "dqa-admin-support", "dqa-admin-moderation", "dqa-admin-settings"];
338
336
  readonly seller: readonly ["dqa-seller-add-product", "dqa-seller-products", "dqa-seller-view-orders", "dqa-seller-analytics", "dqa-seller-add-coupon", "dqa-seller-auctions", "dqa-seller-payout-request", "dqa-seller-shipping", "dqa-seller-messages", "dqa-seller-whatsapp", "dqa-seller-reviews", "dqa-seller-storefront", "dqa-seller-settings"];
339
337
  readonly user: readonly ["dqa-user-view-orders", "dqa-user-view-wishlist", "dqa-user-edit-profile", "dqa-user-add-address", "dqa-user-recently-viewed", "dqa-user-my-catalogue", "dqa-user-my-coupons"];
340
338
  };
@@ -53,7 +53,6 @@ export const ACTION_ID = {
53
53
  // ── Checkout / navigation CTAs ───────────────────────────────────────────
54
54
  CHECKOUT: "checkout",
55
55
  CONTINUE_TO_VERIFY: "continue-to-verification",
56
- SEND_OTP: "send-otp",
57
56
  VERIFY_OTP: "verify-otp",
58
57
  PAY_ONLINE: "pay-online",
59
58
  PAY_COD: "pay-cod",
@@ -99,7 +98,6 @@ export const ACTION_META = {
99
98
  // ── Checkout / navigation CTAs ────────────────────────────────────────────
100
99
  [ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: regLabel("CART", "checkout", "Checkout"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
101
100
  [ACTION_ID.CONTINUE_TO_VERIFY]: { id: ACTION_ID.CONTINUE_TO_VERIFY, label: regLabel("CHECKOUT", "continue-to-verification", "Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
102
- [ACTION_ID.SEND_OTP]: { id: ACTION_ID.SEND_OTP, label: regLabel("CHECKOUT", "send-otp", "Send Code"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
103
101
  [ACTION_ID.VERIFY_OTP]: { id: ACTION_ID.VERIFY_OTP, label: regLabel("CHECKOUT", "verify-otp", "Verify & Continue"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
104
102
  [ACTION_ID.PAY_ONLINE]: { id: ACTION_ID.PAY_ONLINE, label: regLabel("CHECKOUT", "pay-online", "Pay Online"), variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
105
103
  [ACTION_ID.PAY_COD]: { id: ACTION_ID.PAY_COD, label: regLabel("CHECKOUT", "pay-cod", "Cash on Delivery"), variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
@@ -358,7 +356,6 @@ export const DASHBOARD_QUICK_ACTION_ID = {
358
356
  ADMIN_EVENTS: "dqa-admin-events",
359
357
  ADMIN_SUPPORT: "dqa-admin-support",
360
358
  ADMIN_MODERATION: "dqa-admin-moderation",
361
- ADMIN_INTEGRATION_GUIDES: "dqa-admin-integration-guides",
362
359
  ADMIN_USERS: "dqa-admin-users",
363
360
  ADMIN_CATEGORIES: "dqa-admin-categories",
364
361
  ADMIN_REVIEWS: "dqa-admin-reviews",
@@ -404,7 +401,6 @@ export const DASHBOARD_QUICK_ACTION_META = {
404
401
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_EVENTS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_EVENTS, label: "Events", variant: "outline", iconName: "Calendar", requiresAuth: true, requiredRole: "admin", requiredPermission: "events.read" },
405
402
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT, label: "Support Tickets", variant: "outline", iconName: "LifeBuoy", requiresAuth: true, requiredRole: "admin", requiredPermission: "support.read" },
406
403
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION, label: "Moderation", variant: "outline", iconName: "ShieldAlert", requiresAuth: true, requiredRole: "admin", requiredPermission: "moderation.read" },
407
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES, label: "Integration Guides", variant: "ghost", iconName: "BookOpen", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin:site:read" },
408
404
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS, label: "Users", variant: "outline", iconName: "Users", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.read" },
409
405
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_CATEGORIES]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_CATEGORIES, label: "Categories", variant: "outline", iconName: "Tag", requiresAuth: true, requiredRole: "admin", requiredPermission: "categories.read" },
410
406
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_REVIEWS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_REVIEWS, label: "Reviews", variant: "outline", iconName: "Star", requiresAuth: true, requiredRole: "admin", requiredPermission: "reviews.read" },
@@ -454,7 +450,6 @@ export const DASHBOARD_QUICK_ACTIONS = {
454
450
  DASHBOARD_QUICK_ACTION_ID.ADMIN_SECTIONS,
455
451
  DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT,
456
452
  DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION,
457
- DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES,
458
453
  DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS,
459
454
  ],
460
455
  seller: [
@@ -18,6 +18,12 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
18
18
  findByStatus(status: ProductStatus): Promise<ProductDocument[]>;
19
19
  findFeatured(): Promise<ProductDocument[]>;
20
20
  findByCategory(category: string): Promise<ProductDocument[]>;
21
+ /**
22
+ * Published products sharing at least one tag with the given list — "you
23
+ * might also like" style discovery. `array-contains-any` caps at 10 values,
24
+ * so only the first 10 tags are used.
25
+ */
26
+ findByTagsOverlap(tags: string[], limit?: number): Promise<ProductDocument[]>;
21
27
  findByBarcodeId(barcodeId: string): Promise<ProductDocument | null>;
22
28
  findBySlug(slug: string): Promise<ProductDocument | undefined>;
23
29
  findByIdOrSlug(idOrSlug: string): Promise<ProductDocument | undefined>;
@@ -145,6 +145,21 @@ export class ProductRepository extends BaseRepository {
145
145
  .get();
146
146
  return snap.docs.map((d) => this.mapDoc(d));
147
147
  }
148
+ /**
149
+ * Published products sharing at least one tag with the given list — "you
150
+ * might also like" style discovery. `array-contains-any` caps at 10 values,
151
+ * so only the first 10 tags are used.
152
+ */
153
+ async findByTagsOverlap(tags, limit = 8) {
154
+ if (tags.length === 0)
155
+ return [];
156
+ const snap = await this.getCollection()
157
+ .where(PRODUCT_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
158
+ .where(PRODUCT_FIELDS.TAGS, "array-contains-any", tags.slice(0, 10))
159
+ .limit(limit)
160
+ .get();
161
+ return snap.docs.map((d) => this.mapDoc(d));
162
+ }
148
163
  async findByBarcodeId(barcodeId) {
149
164
  const docs = await this.findBy("barcodeId", barcodeId);
150
165
  return docs[0] ?? null;
@@ -226,10 +241,14 @@ export class ProductRepository extends BaseRepository {
226
241
  return snapshot.docs.map((doc) => this.mapDoc(doc));
227
242
  }
228
243
  async findByGroupId(groupId) {
244
+ // Tier GP: groups may mix standard products + pre-orders (never auctions).
229
245
  const snapshot = await this.db
230
246
  .collection(this.collection)
231
247
  .where("groupId", "==", groupId)
232
- .where(PRODUCT_FIELDS.LISTING_TYPE, "==", LISTING_TYPE_VALUES.STANDARD)
248
+ .where(PRODUCT_FIELDS.LISTING_TYPE, "in", [
249
+ LISTING_TYPE_VALUES.STANDARD,
250
+ LISTING_TYPE_VALUES.PRE_ORDER,
251
+ ])
233
252
  .get();
234
253
  return snapshot.docs.map((doc) => this.mapDoc(doc));
235
254
  }
@@ -241,7 +241,7 @@ export interface ProductDocument extends BaseDocument {
241
241
  sublistingIcon?: string;
242
242
  /**
243
243
  * Required since Phase 4 dropped the legacy `isAuction` / `isPreOrder`
244
- * booleans. Every product document carries this; queries route through
244
+ * booleans. Every product document carries this queries route through
245
245
  * `where("listingType", "==", X)` against the `listingType+...` composite
246
246
  * indexes in `appkit/firebase/base/firestore.indexes.json`.
247
247
  * SB-UNI-F — extended with classified | digital-code | live.
@@ -263,18 +263,41 @@ export interface ProductDocument extends BaseDocument {
263
263
  pricePerEntry?: number;
264
264
  prizeMaxEntries?: number;
265
265
  prizeCurrentEntries?: number;
266
+ /**
267
+ * Draw start, set once at creation (= createdAt) — never seller-edited.
268
+ * `prizeRevealWindowEnd` is derived from this + `prizeDrawDurationDays`.
269
+ */
266
270
  prizeRevealWindowStart?: Date;
271
+ /** Hard expiry — `prizeRevealWindowStart` + `prizeDrawDurationDays` days. */
267
272
  prizeRevealWindowEnd?: Date;
273
+ /**
274
+ * "pending" is legacy-only (no longer written by new listings — reveal is
275
+ * fully automatic, there's no seller-facing delayed-start concept anymore —
276
+ * draft→published is how a seller holds a draw back). New listings go
277
+ * straight to "open", flipping to "closed" once every eligible order has
278
+ * been revealed.
279
+ */
268
280
  prizeRevealStatus?: "pending" | "open" | "closed";
269
- prizeRevealDeadlineDays?: number;
281
+ /**
282
+ * Seller-facing duration input, 1–15 days (PRIZE_DRAW_DURATION_DAYS_MIN/MAX
283
+ * in checkout/rules/_limits.ts). Drives `prizeRevealWindowEnd` at creation.
284
+ */
285
+ prizeDrawDurationDays?: number;
270
286
  /** Public proof-of-fairness file (commit-reveal scheme). */
271
287
  prizeGithubFileUrl?: string;
272
288
  /**
273
289
  * Draw mode for prize-draw listings.
274
- * "reveal" = classic buyer-reveal flow (default, backward-compat).
290
+ * "reveal" = classic automatic-reveal flow (default, backward-compat).
275
291
  * "lottery" = user self-pull with weighted random slot assignment.
276
292
  */
277
293
  prizeDrawMode?: "reveal" | "lottery";
294
+ /**
295
+ * Reveal trigger for "reveal" mode draws (irrelevant for "lottery" mode).
296
+ * "instant" — winner assigned the moment the order's payment is confirmed.
297
+ * "scheduled" — winner assigned automatically at whichever comes first:
298
+ * `prizeRevealWindowEnd` or full sellout (`prizeCurrentEntries === prizeMaxEntries`).
299
+ */
300
+ prizeRevealMode?: "instant" | "scheduled";
278
301
  /**
279
302
  * Full lottery config. Only set when prizeDrawMode === "lottery".
280
303
  * Includes server-only price/weight fields — stripped by adapter before any client response.
@@ -333,6 +356,8 @@ export interface PrizeDrawItem {
333
356
  /** Per-slot price in decimal rupees for lottery mode. Lower price = higher weight = higher chance. */
334
357
  prizeSlotPrice?: number;
335
358
  isWon: boolean;
359
+ /** Order id this item was assigned to — set alongside isWon:true. Admin/seller-only mapping (never public) so the seller knows which physical item ships to which order. */
360
+ wonByOrderId?: string;
336
361
  }
337
362
  /** Runtime-accessible product status values — use instead of bare string literals. */
338
363
  export declare const ProductStatusValues: {
@@ -149,6 +149,7 @@ export declare const productItemSchema: z.ZodObject<{
149
149
  condition: z.ZodString;
150
150
  estimatedValue: z.ZodOptional<z.ZodNumber>;
151
151
  isWon: z.ZodBoolean;
152
+ wonByOrderId: z.ZodOptional<z.ZodString>;
152
153
  }, "strip", z.ZodTypeAny, {
153
154
  title: string;
154
155
  images: string[];
@@ -161,6 +162,7 @@ export declare const productItemSchema: z.ZodObject<{
161
162
  } | undefined;
162
163
  description?: string | undefined;
163
164
  estimatedValue?: number | undefined;
165
+ wonByOrderId?: string | undefined;
164
166
  }, {
165
167
  title: string;
166
168
  images: string[];
@@ -173,6 +175,7 @@ export declare const productItemSchema: z.ZodObject<{
173
175
  } | undefined;
174
176
  description?: string | undefined;
175
177
  estimatedValue?: number | undefined;
178
+ wonByOrderId?: string | undefined;
176
179
  }>, "many">>;
177
180
  pricePerEntry: z.ZodOptional<z.ZodNumber>;
178
181
  prizeMaxEntries: z.ZodOptional<z.ZodNumber>;
@@ -180,7 +183,8 @@ export declare const productItemSchema: z.ZodObject<{
180
183
  prizeRevealWindowStart: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
181
184
  prizeRevealWindowEnd: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodDate]>>;
182
185
  prizeRevealStatus: z.ZodOptional<z.ZodEnum<["pending", "open", "closed"]>>;
183
- prizeRevealDeadlineDays: z.ZodOptional<z.ZodNumber>;
186
+ prizeDrawDurationDays: z.ZodOptional<z.ZodNumber>;
187
+ prizeRevealMode: z.ZodOptional<z.ZodEnum<["instant", "scheduled"]>>;
184
188
  prizeGithubFileUrl: z.ZodOptional<z.ZodString>;
185
189
  inStock: z.ZodOptional<z.ZodBoolean>;
186
190
  stockCount: z.ZodOptional<z.ZodNumber>;
@@ -423,6 +427,7 @@ export declare const productItemSchema: z.ZodObject<{
423
427
  } | undefined;
424
428
  description?: string | undefined;
425
429
  estimatedValue?: number | undefined;
430
+ wonByOrderId?: string | undefined;
426
431
  }[] | undefined;
427
432
  pricePerEntry?: number | undefined;
428
433
  prizeMaxEntries?: number | undefined;
@@ -430,8 +435,9 @@ export declare const productItemSchema: z.ZodObject<{
430
435
  prizeRevealWindowStart?: string | Date | undefined;
431
436
  prizeRevealWindowEnd?: string | Date | undefined;
432
437
  prizeRevealStatus?: "closed" | "open" | "pending" | undefined;
433
- prizeRevealDeadlineDays?: number | undefined;
438
+ prizeDrawDurationDays?: number | undefined;
434
439
  prizeGithubFileUrl?: string | undefined;
440
+ prizeRevealMode?: "instant" | "scheduled" | undefined;
435
441
  media?: {
436
442
  type: "video" | "image" | "file";
437
443
  url: string;
@@ -559,6 +565,7 @@ export declare const productItemSchema: z.ZodObject<{
559
565
  } | undefined;
560
566
  description?: string | undefined;
561
567
  estimatedValue?: number | undefined;
568
+ wonByOrderId?: string | undefined;
562
569
  }[] | undefined;
563
570
  pricePerEntry?: number | undefined;
564
571
  prizeMaxEntries?: number | undefined;
@@ -566,8 +573,9 @@ export declare const productItemSchema: z.ZodObject<{
566
573
  prizeRevealWindowStart?: string | Date | undefined;
567
574
  prizeRevealWindowEnd?: string | Date | undefined;
568
575
  prizeRevealStatus?: "closed" | "open" | "pending" | undefined;
569
- prizeRevealDeadlineDays?: number | undefined;
576
+ prizeDrawDurationDays?: number | undefined;
570
577
  prizeGithubFileUrl?: string | undefined;
578
+ prizeRevealMode?: "instant" | "scheduled" | undefined;
571
579
  media?: {
572
580
  type: "video" | "image" | "file";
573
581
  url: string;
@@ -1,6 +1,7 @@
1
1
  export * from "./firestore";
2
2
  import { z } from "zod";
3
3
  import { mediaFieldSchema } from "../../media/types/index";
4
+ import { PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, } from "../../../_internal/shared/checkout/rules/_limits";
4
5
  // --- Sub-schemas --------------------------------------------------------------
5
6
  export const productImageSchema = z.object({
6
7
  url: z.string(),
@@ -96,8 +97,9 @@ export const productItemSchema = z.object({
96
97
  condition: z.string(),
97
98
  estimatedValue: z.number().optional(),
98
99
  isWon: z.boolean(),
100
+ wonByOrderId: z.string().optional(),
99
101
  }))
100
- .min(3)
102
+ .min(2)
101
103
  .max(16)
102
104
  .optional(),
103
105
  pricePerEntry: z.number().positive().optional(),
@@ -106,7 +108,13 @@ export const productItemSchema = z.object({
106
108
  prizeRevealWindowStart: z.union([z.string(), z.date()]).optional(),
107
109
  prizeRevealWindowEnd: z.union([z.string(), z.date()]).optional(),
108
110
  prizeRevealStatus: z.enum(["pending", "open", "closed"]).optional(),
109
- prizeRevealDeadlineDays: z.number().int().positive().optional(),
111
+ prizeDrawDurationDays: z
112
+ .number()
113
+ .int()
114
+ .min(PRIZE_DRAW_DURATION_DAYS_MIN)
115
+ .max(PRIZE_DRAW_DURATION_DAYS_MAX)
116
+ .optional(),
117
+ prizeRevealMode: z.enum(["instant", "scheduled"]).optional(),
110
118
  prizeGithubFileUrl: z.string().url().optional(),
111
119
  inStock: z.boolean().optional(),
112
120
  stockCount: z.number().optional(),
@@ -136,7 +136,8 @@ export interface ProductItem {
136
136
  prizeRevealWindowStart?: string | Date;
137
137
  prizeRevealWindowEnd?: string | Date;
138
138
  prizeRevealStatus?: "pending" | "open" | "closed";
139
- prizeRevealDeadlineDays?: number;
139
+ prizeDrawDurationDays?: number;
140
+ prizeRevealMode?: "instant" | "scheduled";
140
141
  prizeGithubFileUrl?: string;
141
142
  maxPerUser?: number;
142
143
  /** Winner's display name for ended auctions — shown masked in public UI */
@@ -31,6 +31,7 @@ export async function createReview(userId, input) {
31
31
  return reviewRepository.create({
32
32
  productId: input.productId,
33
33
  productTitle: product.title,
34
+ listingType: product.listingType,
34
35
  storeId: product.storeId,
35
36
  storeName: product.storeName,
36
37
  userId,
@@ -4,6 +4,7 @@ import { reviewRepository } from "../../../repositories";
4
4
  import { Container, Div, Heading, Main, Nav, Section, Span, Text } from "../../../ui";
5
5
  import { ROUTES } from "../../../next";
6
6
  import { ReviewDetailShell } from "./ReviewDetailShell";
7
+ import { ReviewCard } from "./ReviewsList";
7
8
  export async function ReviewDetailPageView({ id }) {
8
9
  const doc = await reviewRepository.findById(id).catch(() => null);
9
10
  const review = doc;
@@ -12,5 +13,11 @@ export async function ReviewDetailPageView({ id }) {
12
13
  if (!review || review.status !== "approved") {
13
14
  return (_jsx(Main, { children: _jsx(Section, { padding: "y-6xl", children: _jsx(Container, { size: "sm", children: _jsxs(Div, { className: "text-left", children: [_jsx(Text, { className: "mb-4", size: "5xl", "aria-hidden": "true", children: "\uD83D\uDD0D" }), _jsx(Heading, { color: "inverse", level: 1, className: "text-[var(--appkit-color-text)] dark: mb-2", size: "2xl", weight: "bold", children: "Review not found" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] mb-6", size: "sm", 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-[var(--appkit-space-2)] rounded-lg bg-primary px-[var(--appkit-space-5)] py-[var(--appkit-space-2-5)] text-[length:var(--appkit-text-sm)] font-medium text-white hover:bg-primary-600 transition-colors", children: "\u2190 Back to Reviews" })] }) }) }) }));
14
15
  }
15
- return (_jsxs(Main, { children: [_jsx(Div, { paddingY: "y-xs-tall", className: "border-b border-neutral-100 bg-neutral-50", padding: "x-md", children: _jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "xs", color: "faint", className: "mx-auto max-w-3xl", "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 truncate max-w-[200px]", children: review.title ?? review.id })] }) }), _jsx(ReviewDetailShell, { review: review, storeHref: storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null })] }));
16
+ const [sameProductDocs, sameStoreDocs] = await Promise.all([
17
+ reviewRepository.findApprovedByProduct(review.productId).catch(() => []),
18
+ storeSlug ? reviewRepository.findApprovedByStore(storeSlug).catch(() => []) : Promise.resolve([]),
19
+ ]);
20
+ const sameProductReviews = sameProductDocs.filter((r) => r.id !== review.id).slice(0, 6);
21
+ const sameStoreReviews = sameStoreDocs.filter((r) => r.id !== review.id).slice(0, 6);
22
+ return (_jsxs(Main, { children: [_jsx(Div, { paddingY: "y-xs-tall", className: "border-b border-neutral-100 bg-neutral-50", padding: "x-md", children: _jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "xs", color: "faint", className: "mx-auto max-w-3xl", "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 truncate max-w-[200px]", children: review.title ?? review.id })] }) }), _jsx(ReviewDetailShell, { review: review, storeHref: storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null }), sameProductReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.productTitle ?? "this product"] }), _jsx(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: sameProductReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "listing" }, r.id))) })] }) })), sameStoreReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.storeName ?? "this store"] }), _jsx(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: sameStoreReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "store" }, r.id))) })] }) }))] }));
16
23
  }
@@ -24,6 +24,7 @@ import { maskName } from "../../../security";
24
24
  import { getDefaultLocale } from "../../../core/baseline-resolver";
25
25
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
26
26
  import { ROUTES } from "../../../next";
27
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
27
28
  import { apiClient } from "../../../http";
28
29
  import { REVIEW_ENDPOINTS } from "../../../constants/api-endpoints";
29
30
  export function ReviewDetailShell({ review, storeHref }) {
@@ -84,7 +85,7 @@ export function ReviewDetailShell({ review, storeHref }) {
84
85
  };
85
86
  // ── Links ───────────────────────────────────────────────────────────────────
86
87
  const productHref = review.productId
87
- ? String(ROUTES.PUBLIC.PRODUCT_DETAIL(review.productId))
88
+ ? pluginFor(review.listingType ?? "standard").detailRoute(review.productId)
88
89
  : null;
89
90
  const sellerHref = storeHref ?? null;
90
91
  const reviewerProfileId = review.userSlug ?? review.userId;
@@ -7,6 +7,7 @@ import { maskName } from "../../../security";
7
7
  import { getDefaultLocale } from "../../../core/baseline-resolver";
8
8
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
9
9
  import { ROUTES } from "../../../next";
10
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
10
11
  export function ReviewCard({ review, context = "general", className = "" }) {
11
12
  const date = review.createdAt
12
13
  ? new Date(review.createdAt).toLocaleDateString(getDefaultLocale(), {
@@ -19,7 +20,7 @@ export function ReviewCard({ review, context = "general", className = "" }) {
19
20
  const initials = displayName.charAt(0).toUpperCase();
20
21
  const reviewHref = String(ROUTES.PUBLIC.REVIEW_DETAIL(review.id));
21
22
  const productHref = review.productId
22
- ? String(ROUTES.PUBLIC.PRODUCT_DETAIL(review.productId))
23
+ ? pluginFor(review.listingType ?? "standard").detailRoute(review.productId)
23
24
  : null;
24
25
  const profileHref = !review.isAnonymous && review.userId
25
26
  ? String(ROUTES.PUBLIC.PROFILE(review.userId))
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { ReviewStatus } from "../types";
5
5
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
6
+ import type { ListingType } from "../../products/types/index";
6
7
  /** Runtime-accessible review status values — use instead of bare string literals. */
7
8
  export declare const ReviewStatusValues: {
8
9
  readonly PENDING: "pending";
@@ -19,6 +20,8 @@ export interface ReviewVideoField {
19
20
  export interface ReviewDocument extends BaseDocument {
20
21
  productId: string;
21
22
  productTitle: string;
23
+ /** Denormalized from the product at review-creation time — drives the correct detail-page link (e.g. prize-draw items link to /prize-draws/[slug], not /products/[slug]). */
24
+ listingType?: ListingType;
22
25
  storeId?: string;
23
26
  storeName?: string;
24
27
  userId: string;