@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.
- package/dist/_internal/client/features/layout/navActive.d.ts +17 -0
- package/dist/_internal/client/features/layout/navActive.js +33 -0
- package/dist/_internal/server/features/auth/actions.d.ts +0 -1
- package/dist/_internal/server/features/auth/actions.js +0 -1
- package/dist/_internal/server/features/bundles/data.d.ts +22 -4
- package/dist/_internal/server/features/bundles/data.js +41 -4
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +7 -78
- package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -16
- package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -30
- package/dist/_internal/server/features/orders/adapters.js +1 -14
- package/dist/_internal/server/features/products/actions.js +8 -3
- package/dist/_internal/server/features/products/service.d.ts +15 -0
- package/dist/_internal/server/features/products/service.js +41 -0
- package/dist/_internal/server/functions/firestore.d.ts +3 -1
- package/dist/_internal/server/functions/firestore.js +17 -1
- package/dist/_internal/server/functions/scheduled.d.ts +2 -5
- package/dist/_internal/server/functions/scheduled.js +6 -30
- package/dist/_internal/server/jobs/core/bundleStockSync.js +25 -7
- package/dist/_internal/server/jobs/core/countersReconcile.js +7 -1
- package/dist/_internal/server/jobs/core/index.d.ts +4 -4
- package/dist/_internal/server/jobs/core/index.js +4 -4
- package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.d.ts +19 -0
- package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.js +30 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.d.ts +2 -2
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +2 -2
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.d.ts +20 -0
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +113 -0
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.d.ts +14 -0
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +74 -0
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.d.ts +22 -0
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +68 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +3 -4
- package/dist/_internal/server/jobs/handlers/index.js +8 -6
- package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.js +11 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.js +2 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.js +4 -0
- package/dist/_internal/shared/actions/action-registry.js +40 -10
- package/dist/_internal/shared/checkout/rules/_defaults.js +1 -2
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +3 -0
- package/dist/_internal/shared/checkout/rules/_limits.js +3 -0
- package/dist/_internal/shared/checkout/rules/_registry.d.ts +2 -1
- package/dist/_internal/shared/checkout/rules/_registry.js +2 -2
- package/dist/_internal/shared/checkout/rules/classified.rule.js +0 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +20 -1
- package/dist/_internal/shared/checkout/rules/types.d.ts +17 -7
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +2 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +2 -0
- package/dist/_internal/shared/features/categories/bundle-pricing.d.ts +23 -0
- package/dist/_internal/shared/features/categories/bundle-pricing.js +25 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
- package/dist/constants/api-endpoints.d.ts +6 -9
- package/dist/constants/api-endpoints.js +5 -5
- package/dist/constants/field-names.d.ts +17 -1
- package/dist/constants/field-names.js +17 -1
- package/dist/features/account/components/UserSidebar.js +21 -9
- package/dist/features/account/hooks/useNotifications.js +5 -0
- package/dist/features/admin/actions/admin-actions.js +6 -0
- package/dist/features/admin/actions/notification-actions.js +1 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
- package/dist/features/admin/components/AdminGuideHubView.js +15 -1
- package/dist/features/admin/components/AdminPaymentsGuideView.d.ts +2 -0
- package/dist/features/admin/components/AdminPaymentsGuideView.js +36 -0
- package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
- package/dist/features/admin/components/AdminSidebar.js +18 -9
- package/dist/features/admin/components/AdminSiteSettingsView.js +33 -6
- package/dist/features/admin/components/AdminTesterChecklistView.js +74 -22
- package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +2 -0
- package/dist/features/admin/components/AdminWhatsAppGuideView.js +35 -0
- package/dist/features/admin/hooks/useAdminListing.d.ts +4 -0
- package/dist/features/admin/hooks/useAdminListing.js +4 -2
- package/dist/features/admin/schemas/firestore.d.ts +13 -5
- package/dist/features/admin/schemas/firestore.js +1 -2
- package/dist/features/auctions/actions/bid-actions.js +3 -1
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +6 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +8 -4
- package/dist/features/auctions/components/AuctionDetailPageView.js +10 -13
- package/dist/features/auctions/components/CollapsibleBidHistory.d.ts +23 -2
- package/dist/features/auctions/components/CollapsibleBidHistory.js +22 -3
- package/dist/features/auctions/components/LiveBidPrice.d.ts +19 -0
- package/dist/features/auctions/components/LiveBidPrice.js +17 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +16 -1
- package/dist/features/auctions/hooks/useBids.d.ts +33 -0
- package/dist/features/auctions/hooks/useBids.js +32 -0
- package/dist/features/auctions/hooks/useLiveAuctionBid.d.ts +20 -0
- package/dist/features/auctions/hooks/useLiveAuctionBid.js +23 -0
- package/dist/features/auth/checkout-value-otp.d.ts +7 -10
- package/dist/features/auth/checkout-value-otp.js +14 -10
- package/dist/features/auth/server.d.ts +0 -1
- package/dist/features/auth/server.js +0 -1
- package/dist/features/blog/api/[slug]/route.d.ts +5 -0
- package/dist/features/blog/api/[slug]/route.js +12 -3
- package/dist/features/blog/components/BlogPostView.d.ts +2 -0
- package/dist/features/blog/components/BlogPostView.js +5 -4
- package/dist/features/blog/hooks/useBlog.d.ts +2 -0
- package/dist/features/blog/hooks/useBlog.js +2 -0
- package/dist/features/blog/repository/blog.repository.d.ts +7 -0
- package/dist/features/blog/repository/blog.repository.js +44 -0
- package/dist/features/blog/schemas/firestore.d.ts +3 -0
- package/dist/features/blog/schemas/firestore.js +1 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/index.d.ts +0 -2
- package/dist/features/cart/components/index.js +0 -1
- package/dist/features/cart/hooks/useCartCount.js +22 -5
- package/dist/features/cart/hooks/useGuestCart.js +11 -1
- package/dist/features/cart/utils/guest-cart.d.ts +4 -0
- package/dist/features/cart/utils/guest-cart.js +10 -0
- package/dist/features/cart/utils/pending-ops.d.ts +17 -0
- package/dist/features/cart/utils/pending-ops.js +46 -5
- package/dist/features/categories/components/BrandDetailPageView.js +6 -2
- package/dist/features/categories/components/BundleDetailView.js +4 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +10 -2
- package/dist/features/categories/schemas/firestore.d.ts +7 -0
- package/dist/features/checkout/actions/index.d.ts +0 -1
- package/dist/features/checkout/actions/index.js +0 -1
- package/dist/features/checkout/schemas/firestore.d.ts +2 -2
- package/dist/features/events/actions/event-actions.d.ts +2 -0
- package/dist/features/events/actions/event-actions.js +5 -0
- package/dist/features/events/components/EventCard.js +2 -3
- package/dist/features/events/components/RelatedEventsCarousel.d.ts +15 -0
- package/dist/features/events/components/RelatedEventsCarousel.js +13 -0
- package/dist/features/events/components/index.d.ts +2 -0
- package/dist/features/events/components/index.js +1 -0
- package/dist/features/events/repository/events.repository.d.ts +6 -0
- package/dist/features/events/repository/events.repository.js +21 -0
- package/dist/features/history/repository/user-history.repository.d.ts +1 -1
- package/dist/features/history/utils/guest-history.d.ts +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +4 -2
- package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +44 -10
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +2 -2
- package/dist/features/media/MediaVideo.d.ts +7 -0
- package/dist/features/media/MediaVideo.js +36 -5
- package/dist/features/orders/components/OrdersList.js +1 -5
- package/dist/features/orders/schemas/firestore.d.ts +2 -8
- package/dist/features/orders/schemas/index.d.ts +3 -32
- package/dist/features/orders/schemas/index.js +1 -4
- package/dist/features/orders/types/index.d.ts +6 -9
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
- package/dist/features/products/actions/product-actions.js +13 -10
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +1 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +7 -4
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
- package/dist/features/products/components/PrizeDrawWinnerMappingView.d.ts +14 -0
- package/dist/features/products/components/PrizeDrawWinnerMappingView.js +37 -0
- package/dist/features/products/components/PrizeRevealModal.d.ts +10 -23
- package/dist/features/products/components/PrizeRevealModal.js +18 -147
- package/dist/features/products/components/ProductDetailPageView.js +72 -30
- package/dist/features/products/components/ProductForm.js +7 -14
- package/dist/features/products/components/ProductGalleryClient.d.ts +10 -1
- package/dist/features/products/components/ProductGalleryClient.js +29 -10
- package/dist/features/products/components/ProductsIndexListing.js +2 -1
- package/dist/features/products/components/RelatedProductsCarousel.js +2 -2
- package/dist/features/products/components/ShowGroupSection.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +1 -1
- package/dist/features/products/components/index.d.ts +4 -2
- package/dist/features/products/components/index.js +1 -0
- package/dist/features/products/constants/action-defs.d.ts +1 -3
- package/dist/features/products/constants/action-defs.js +0 -5
- package/dist/features/products/repository/products.repository.d.ts +6 -0
- package/dist/features/products/repository/products.repository.js +20 -1
- package/dist/features/products/schemas/firestore.d.ts +28 -3
- package/dist/features/products/schemas/index.d.ts +11 -3
- package/dist/features/products/schemas/index.js +10 -2
- package/dist/features/products/types/index.d.ts +2 -1
- package/dist/features/reviews/actions/review-actions.js +1 -0
- package/dist/features/reviews/components/ReviewDetailPageView.js +8 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +2 -1
- package/dist/features/reviews/components/ReviewsList.js +2 -1
- package/dist/features/reviews/schemas/firestore.d.ts +3 -0
- package/dist/features/reviews/schemas/index.d.ts +6 -0
- package/dist/features/reviews/schemas/index.js +2 -0
- package/dist/features/reviews/types/index.d.ts +3 -0
- package/dist/features/scams/actions/scam-actions.d.ts +3 -0
- package/dist/features/scams/actions/scam-actions.js +3 -2
- package/dist/features/scams/components/ScamProfileView.d.ts +2 -1
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.d.ts +6 -0
- package/dist/features/scams/repository/scammer.repository.js +24 -0
- package/dist/features/seller/actions/seller-actions.js +8 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -0
- package/dist/features/seller/components/SellerSidebar.js +20 -9
- package/dist/features/stores/components/StoreDetailLayoutView.js +6 -6
- package/dist/features/stores/components/StoreGuideHubView.js +7 -1
- package/dist/features/stores/components/StoreNavTabs.d.ts +10 -1
- package/dist/features/stores/components/StoreNavTabs.js +22 -9
- package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +3 -0
- package/dist/features/stores/components/StoreWhatsAppGuideView.js +12 -0
- package/dist/features/stores/components/index.d.ts +2 -0
- package/dist/features/stores/components/index.js +1 -0
- package/dist/features/tester/actions/index.d.ts +1 -0
- package/dist/features/tester/actions/index.js +1 -0
- package/dist/features/tester/actions/leaderboard-actions.d.ts +3 -0
- package/dist/features/tester/actions/leaderboard-actions.js +5 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +26 -4
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +13 -0
- package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +23 -0
- package/dist/features/tester/components/BugHunterLeaderboardView.js +18 -0
- package/dist/features/tester/components/index.d.ts +2 -0
- package/dist/features/tester/components/index.js +1 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +12 -1
- package/dist/features/tester/repository/tester-checklist-item.repository.js +80 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +4 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.js +4 -0
- package/dist/features/tester/schemas/firestore.d.ts +22 -1
- package/dist/features/tester/schemas/firestore.js +8 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +42 -7
- package/dist/features/tester/seed-data/events-tester-seed-data.js +23 -0
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +69 -31
- package/dist/features/tester/seed-data/products-tester-seed-data.js +154 -9
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +252 -15
- package/dist/features/wishlist/hooks/useGuestWishlist.js +11 -1
- package/dist/features/wishlist/hooks/useWishlistCount.js +16 -2
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -0
- package/dist/features/wishlist/utils/guest-wishlist.js +11 -0
- package/dist/index.d.ts +23 -22
- package/dist/index.js +31 -58
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +2 -2
- package/dist/next/routing/route-map.d.ts +9 -2
- package/dist/next/routing/route-map.js +4 -1
- package/dist/schemas/registry.d.ts +6 -26
- package/dist/seed/bids-seed-data.js +14 -2
- package/dist/seed/blog-posts-seed-data.js +2 -1
- package/dist/seed/categories-seed-data.js +11 -2
- package/dist/seed/events-seed-data.js +11 -11
- package/dist/seed/faq-seed-data.js +5 -1
- package/dist/seed/products-auctions-seed-data.js +2 -2
- package/dist/seed/products-preorders-seed-data.d.ts +2 -1
- package/dist/seed/products-prize-draws-seed-data.js +56 -10
- package/dist/seed/products-standard-seed-data.js +80 -3
- package/dist/seed/scammers-seed-data.js +75 -2
- package/dist/seed/site-settings-seed-data.js +5 -2
- package/dist/server-entry.d.ts +0 -1
- package/dist/server-entry.js +0 -1
- package/dist/server.d.ts +4 -20
- package/dist/server.js +10 -57
- package/dist/styles.css +1 -277
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.js +1 -1
- package/dist/ui/components/ImageLightbox.d.ts +9 -0
- package/dist/ui/components/ImageLightbox.js +6 -3
- package/dist/ui/components/ListingToolbar.d.ts +18 -1
- package/dist/ui/components/ListingToolbar.js +5 -2
- package/dist/ui/components/StickyToolbar.js +13 -8
- 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
|
|
16
|
-
src,
|
|
17
|
-
|
|
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 (
|
|
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
|
|
23
|
-
return (_jsxs(Stack, { gap: "3", children: [
|
|
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":
|
|
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))) })),
|
|
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, " / ",
|
|
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) =>
|
|
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 {
|
|
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 ?
|
|
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 \
|
|
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 ? "
|
|
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 \
|
|
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,
|
|
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-
|
|
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, "
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 */
|
|
@@ -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
|
-
|
|
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
|
-
?
|
|
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
|
-
?
|
|
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;
|