@mohasinac/appkit 3.3.6 → 3.4.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/classified/ClassifiedDetailView.js +2 -1
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +2 -1
- package/dist/_internal/client/features/live/LiveItemDetailView.js +2 -1
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +1 -1
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +2 -1
- package/dist/_internal/client/features/lottery/LotteryListView.js +2 -1
- package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +2 -1
- package/dist/_internal/server/features/checkout/actions.js +0 -1
- package/dist/_internal/server/features/lottery/actions.js +0 -2
- package/dist/_internal/server/features/reviews/actions.js +1 -1
- package/dist/features/about/components/FAQPageView.js +0 -2
- package/dist/features/about/components/FeesView.js +13 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +17 -3
- package/dist/features/about/components/HowEmiWorksView.d.ts +3 -0
- package/dist/features/about/components/HowEmiWorksView.js +117 -0
- package/dist/features/about/components/PublicProfileView.js +1 -4
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +1 -0
- package/dist/features/account/components/BecomeSellerView.d.ts +1 -1
- package/dist/features/account/components/BecomeSellerView.js +1 -1
- package/dist/features/account/components/NotificationBell.js +1 -2
- package/dist/features/account/components/UserSupportView.js +0 -1
- package/dist/features/admin/components/AdminAdsView.js +3 -3
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +0 -4
- package/dist/features/admin/components/AdminCarouselEditorView.js +3 -3
- package/dist/features/admin/components/AdminCarouselView.js +3 -2
- package/dist/features/admin/components/AdminGuideHubView.js +2 -2
- package/dist/features/admin/components/AdminMediaView.js +2 -4
- package/dist/features/admin/components/AdminUserEditorView.d.ts +1 -1
- package/dist/features/admin/components/AdminUserEditorView.js +1 -1
- package/dist/features/admin/components/QuickActionsPanel.js +2 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +2 -1
- package/dist/features/blog/components/BlogIndexListing.js +1 -1
- package/dist/features/blog/components/BlogListView.js +2 -1
- package/dist/features/blog/components/BlogPostForm.js +1 -1
- package/dist/features/blog/components/BlogPostView.js +2 -1
- package/dist/features/cart/components/CartDrawer.js +2 -1
- package/dist/features/categories/components/BrandDetailPageView.js +2 -1
- package/dist/features/categories/components/BundleCollage.js +2 -3
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -5
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryGrid.js +2 -1
- package/dist/features/categories/components/CategoryProductsView.d.ts +1 -1
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/ConcernCard.js +2 -1
- package/dist/features/collections/components/CollectionCard.js +2 -1
- package/dist/features/consultation/components/ConsultationForm.js +0 -1
- package/dist/features/contact/components/ContactInfoSidebar.js +2 -2
- package/dist/features/copilot/components/AdminCopilotView.js +0 -9
- package/dist/features/events/components/EventFormDrawer.d.ts +1 -1
- package/dist/features/events/components/EventFormDrawer.js +1 -1
- package/dist/features/events/components/EventPollWidget.js +2 -2
- package/dist/features/events/components/EventsIndexListing.js +1 -1
- package/dist/features/faq/actions/faq-actions.d.ts +2 -10
- package/dist/features/faq/actions/faq-actions.js +0 -4
- package/dist/features/homepage/components/AdSlot.js +2 -1
- package/dist/features/homepage/components/CustomCardsSection.js +2 -3
- package/dist/features/homepage/components/FeaturedBundlesSection.js +2 -3
- package/dist/features/homepage/components/GoogleReviewsSection.js +4 -3
- package/dist/features/homepage/components/HeroBanner.js +0 -1
- package/dist/features/homepage/components/SectionCarousel.d.ts +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -2
- package/dist/features/homepage/components/SocialFeedSection.js +2 -5
- package/dist/features/homepage/components/SocialPostCard.js +4 -3
- package/dist/features/media/MediaImage.d.ts +10 -1
- package/dist/features/media/MediaImage.js +6 -2
- package/dist/features/media/MediaPickerModal.js +2 -3
- package/dist/features/media/upload/MediaUploadList.js +2 -2
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -0
- package/dist/features/products/api/route.js +1 -2
- package/dist/features/products/components/CompareOverlay.js +2 -3
- package/dist/features/products/components/CustomSectionTabContent.js +2 -2
- package/dist/features/products/components/FeatureBadge.js +0 -6
- package/dist/features/products/components/GroupSettingsPanel.js +2 -3
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -3
- package/dist/features/products/components/PrizeDrawCollage.js +2 -3
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +3 -3
- package/dist/features/products/components/PrizeRevealModal.js +3 -4
- package/dist/features/products/components/ProductDetailActions.d.ts +1 -1
- package/dist/features/products/components/ProductDetailActions.js +1 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
- package/dist/features/products/components/ProductDetailPageView.js +3 -2
- package/dist/features/products/components/ProductFeatureBadges.d.ts +7 -1
- package/dist/features/products/components/ProductFeatureBadges.js +16 -3
- package/dist/features/products/components/ProductGradingTab.js +2 -2
- package/dist/features/products/components/ProductsIndexPageView.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +3 -4
- package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -1
- package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +4 -3
- package/dist/features/reviews/components/ReviewModal.js +2 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +2 -1
- package/dist/features/scams/components/ScamAwarenessModal.js +2 -2
- package/dist/features/search/components/SearchView.d.ts +1 -1
- package/dist/features/search/components/SearchView.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +1 -5
- package/dist/features/seller/components/SellerPreOrdersView.js +3 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +3 -2
- package/dist/features/seller/components/SellerProductsView.js +2 -1
- package/dist/features/seller/components/SellerSidebar.js +5 -4
- package/dist/features/shell/StepForm.js +1 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +2 -3
- package/dist/features/stores/components/StoreAboutView.js +2 -2
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +2 -2
- package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
- package/dist/features/stores/components/StoreGuideHubView.js +2 -2
- package/dist/features/stores/components/StoreHeader.js +2 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +2 -2
- package/dist/features/stores/components/StoreNavTabs.js +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +2 -2
- package/dist/features/stores/components/StoreSettingsGuideView.js +3 -3
- package/dist/features/wishlist/components/WishlistPage.js +2 -1
- package/dist/features/wishlist/components/WishlistView.js +0 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/seed/factories/product.factory.js +0 -32
- package/dist/seed/faq-seed-data.js +23 -4
- package/dist/styles.css +4 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.js +6 -458
- package/dist/ui/DataTable.js +0 -21
- package/dist/ui/components/Anchor.d.ts +3 -1
- package/dist/ui/components/Anchor.js +1 -0
- package/dist/ui/components/Avatar.style.css +3 -0
- package/dist/ui/components/Breadcrumb.js +2 -1
- package/dist/ui/components/FormActionBar.js +2 -1
- package/dist/ui/components/ListingLayout.d.ts +1 -1
- package/dist/ui/components/ListingLayout.js +1 -1
- package/dist/ui/components/Motion.js +1 -1
- package/dist/ui/components/QuickCreateModal.js +2 -1
- package/dist/ui/components/SlottedListingView.d.ts +1 -1
- package/dist/ui/components/SlottedListingView.js +1 -2
- package/package.json +1 -1
|
@@ -36,12 +36,6 @@ function FeatureIcon({ icon, className = ICON_SIZE_CLASS, }) {
|
|
|
36
36
|
const Cmp = FEATURE_ICON_MAP[icon] ?? FEATURE_ICON_MAP.tag;
|
|
37
37
|
return _jsx(Cmp, { className: className });
|
|
38
38
|
}
|
|
39
|
-
function colorStyleFor(iconColor) {
|
|
40
|
-
if (!iconColor)
|
|
41
|
-
return undefined;
|
|
42
|
-
const ref = `var(${iconColor})`;
|
|
43
|
-
return { color: ref, borderColor: ref };
|
|
44
|
-
}
|
|
45
39
|
export function FeatureBadge({ featureId, features }) {
|
|
46
40
|
const f = features.find((x) => x.id === featureId);
|
|
47
41
|
const spanRef = useRef(null);
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { Button, Code, ConfirmDeleteModal, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Span, Stack, Tabs, TabsContent, TabsList, TabsTrigger, Text, useToast } from "../../../ui";
|
|
6
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
6
7
|
import { apiClient } from "../../../http";
|
|
7
8
|
import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
|
|
8
9
|
const __P = {
|
|
@@ -216,9 +217,7 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
216
217
|
setOpen(next);
|
|
217
218
|
if (next && isGroupParent && children === null)
|
|
218
219
|
loadChildren();
|
|
219
|
-
}, className: "w-full flex items-center justify-between py-2 text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (
|
|
220
|
-
// eslint-disable-next-line @next/next/no-img-element
|
|
221
|
-
_jsx("img", { src: child.images[0], alt: child.title, className: "w-8 h-8 rounded-full object-cover border border-[var(--appkit-color-border)]" })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-xs`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
|
|
220
|
+
}, className: "w-full flex items-center justify-between py-2 text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (_jsx(Div, { className: "relative w-8 h-8 flex-shrink-0 overflow-hidden border border-[var(--appkit-color-border)]", rounded: "full", children: _jsx(MediaImage, { src: child.images[0], alt: child.title, size: "thumbnail" }) })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-xs`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
|
|
222
221
|
}
|
|
223
222
|
function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, storeProductsEndpoint, linkTargets, setLinkTargets, onAddCreate, onAddLink, loading, }) {
|
|
224
223
|
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "childTitle", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "childPrice", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "dark:text-[var(--appkit-color-text-faint)]/80", color: "faint", size: "xs", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { size: "xs", color: "muted", children: "Search your existing products or pre-orders. Auctions cannot be linked. Select multiple to bulk-link." }), _jsx(ProductInlineSelect, { scope: "store", endpoint: storeProductsEndpoint, filters: "listingType==standard", multiple: true, value: linkTargets, onChange: setLinkTargets, placeholder: "Search listings\u2026" }), _jsx(Button, { type: "button", onClick: onAddLink, isLoading: loading, disabled: linkTargets.length === 0, children: linkTargets.length > 1 ? `Link ${linkTargets.length} to group` : "Link to group" })] }) })] }) }));
|
|
@@ -10,6 +10,7 @@ const CLS_BUNDLE_PILL = "inline-flex items-center rounded-full bg-violet-600 px-
|
|
|
10
10
|
const CLS_STOCK_OK = "bg-success-surface text-white";
|
|
11
11
|
const CLS_VIEW_BTN = "mt-2 w-full cursor-pointer rounded-md bg-violet-600 py-1.5 text-center text-xs font-semibold text-white transition-colors hover:bg-violet-700 active:scale-[0.98]";
|
|
12
12
|
import { BaseListingCard, Div, Row, Span, Stack, Text, TextLink } from "../../../ui";
|
|
13
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
13
14
|
const DEFAULT_LABELS = {
|
|
14
15
|
bundleBadge: "Bundle",
|
|
15
16
|
inStockBadge: "In stock",
|
|
@@ -44,7 +45,7 @@ export function MarketplaceBundleCard({ bundle, className = "", variant = "grid"
|
|
|
44
45
|
}
|
|
45
46
|
router.push(String(detailHref));
|
|
46
47
|
}, [detailHref, onNavigate, router]);
|
|
47
|
-
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: showCollage ? (_jsx(Div, { layout: "grid", className: `h-full w-full gap-[0.125rem] bg-[var(--appkit-color-surface-muted)] ${collageTiles.length === 2 ? "grid-cols-2 grid-rows-1" : "grid-cols-2 grid-rows-2"}`, children: collageTiles.map((tile, i) => (_jsxs(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: [_jsx(
|
|
48
|
+
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: showCollage ? (_jsx(Div, { layout: "grid", className: `h-full w-full gap-[0.125rem] bg-[var(--appkit-color-surface-muted)] ${collageTiles.length === 2 ? "grid-cols-2 grid-rows-1" : "grid-cols-2 grid-rows-2"}`, children: collageTiles.map((tile, i) => (_jsxs(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: [_jsx(MediaImage, { src: tile.imageURL, alt: tile.title ?? `${bundle.name} item ${i + 1}`, size: "card" }), i === collageTiles.length - 1 && overflow > 0 && (_jsxs(Row, { textWeight: "semibold", textSize: "sm", surface: "overlay-md", className: "absolute inset-0 text-white", align: "center", justify: "center", children: ["+", overflow] }))] }, `${tile.productId}-${i}`))) })) : cover ? (_jsx(Div, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", children: _jsx(MediaImage, { src: cover, alt: bundle.name, size: "card" }) })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No image" }) })) }), _jsxs(Stack, { className: "absolute right-2 top-2", align: "end", gap: "xs", children: [_jsx(Span, { size: "xs", weight: "medium", className: CLS_BUNDLE_PILL, children: mergedLabels.bundleBadge }), _jsx(Span, { layout: "inline-flex", color: "inverse", className: `${stock === "out_of_stock" ? "bg-neutral-500 " : CLS_STOCK_OK}`, rounded: "full", padding: "pill-xs", size: "xs", weight: "medium", children: stock === "out_of_stock" ? mergedLabels.outOfStockBadge : mergedLabels.inStockBadge })] }), stock === "out_of_stock" && (_jsx(Row, { surface: "overlay-xs", className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Span, { color: "inverse", size: "xs", weight: "bold", className: "bg-neutral-900/80 tracking-wider", rounded: "default", padding: "pill-md", transform: "uppercase", children: mergedLabels.outOfStockBadge }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
48
49
|
event.stopPropagation();
|
|
49
50
|
onSelect(bundle.id, !isSelected);
|
|
50
51
|
}, className: selectable || isSelected
|
|
@@ -9,6 +9,7 @@ import { formatCurrency } from "../../../utils";
|
|
|
9
9
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
10
10
|
const CLS_PRIZE_PILL = "inline-flex items-center rounded-full bg-fuchsia-600 px-2 py-0.5 text-white";
|
|
11
11
|
import { BaseListingCard, Button, Div, Row, Span, Stack, Text, TextLink } from "../../../ui";
|
|
12
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
12
13
|
const DEFAULT_LABELS = {
|
|
13
14
|
prizeDrawBadge: "Prize draw",
|
|
14
15
|
pendingBadge: "Reveal pending",
|
|
@@ -91,9 +92,7 @@ export function MarketplacePrizeDrawCard({ product, className = "", variant = "g
|
|
|
91
92
|
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: thumbItems.length > 0 ? (_jsx(Div, { layout: "grid", className: "grid-cols-2 grid-rows-2 gap-[0.125rem] h-full w-full bg-[var(--appkit-color-surface-muted)]", children: Array.from({ length: 4 }).map((_, i) => {
|
|
92
93
|
const it = thumbItems[i];
|
|
93
94
|
const img = it?.images?.[0];
|
|
94
|
-
return (_jsx(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: img ? (
|
|
95
|
-
/* eslint-disable-next-line @next/next/no-img-element */
|
|
96
|
-
_jsx("img", { src: img, alt: it?.title ?? `Prize ${i + 1}`, loading: "lazy", className: "absolute inset-0 h-full w-full object-cover" })) : null }, `thumb-${i}`));
|
|
95
|
+
return (_jsx(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: img ? (_jsx(MediaImage, { src: img, alt: it?.title ?? `Prize ${i + 1}`, size: "card" })) : null }, `thumb-${i}`));
|
|
97
96
|
}) })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No prizes" }) })) }), _jsxs(Stack, { className: "absolute right-2 top-2", align: "end", gap: "xs", children: [_jsx(Span, { size: "xs", weight: "medium", className: CLS_PRIZE_PILL, children: mergedLabels.prizeDrawBadge }), _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", className: `${statusVariant(status)}`, rounded: "full", padding: "pill-xs", children: statusLabel(status, mergedLabels) })] }), status === "closed" ? (_jsx(Row, { surface: "overlay-xs", className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Span, { color: "inverse", size: "xs", weight: "bold", className: "bg-neutral-900/80 tracking-wider", rounded: "default", padding: "pill-md", transform: "uppercase", children: mergedLabels.closedBadge }) })) : null, onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
98
97
|
event.stopPropagation();
|
|
99
98
|
onSelect(product.id, !isSelected);
|
|
@@ -17,6 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
17
17
|
import { Row } from "@mohasinac/appkit";
|
|
18
18
|
import { useState } from "react";
|
|
19
19
|
import { Div, Scrim, Text } from "../../../ui";
|
|
20
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
20
21
|
import { ImageLightbox } from "../../../ui/components/ImageLightbox";
|
|
21
22
|
const __P = {
|
|
22
23
|
p6: "p-6",
|
|
@@ -59,9 +60,7 @@ export function PrizeDrawCollage({ items, highlightItemNumber, onItemClick, wonL
|
|
|
59
60
|
isHighlight
|
|
60
61
|
? "ring-2 ring-offset-2 ring-[var(--appkit-color-primary)] scale-[1.02]"
|
|
61
62
|
: "hover:scale-[1.01]",
|
|
62
|
-
].join(" "), "aria-label": `View ${it.title || `prize #${it.itemNumber}`} in lightbox`, children: [_jsxs(Div, { className: "relative aspect-square w-full", children: [cover ? (
|
|
63
|
-
/* eslint-disable-next-line @next/next/no-img-element */
|
|
64
|
-
_jsx("img", { src: cover, alt: it.title || `Prize #${it.itemNumber}`, className: "absolute inset-0 h-full w-full object-cover", loading: "lazy" })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No image" }) })), _jsxs(Div, { textWeight: "semibold", textSize: "xs", surface: "overlay-xl", padding: "chip-2xs", className: "absolute left-2 top-2 text-white", rounded: "default", children: ["#", it.itemNumber] }), it.isWon && !hideWonState ? (_jsxs(_Fragment, { children: [_jsx(Scrim, { direction: "diagonal", intensity: "medium", multiply: true, className: "absolute inset-0" }), _jsx(Row, { className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Text, { className: CLS_WON_STAMP, children: wonLabel }) })] })) : null] }), _jsxs(Div, { padding: "xs", children: [_jsx(Text, { className: "line-clamp-2", size: "sm", weight: "medium", children: it.title || `Prize #${it.itemNumber}` }), it.estimatedValue != null ? (_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["est. \u20B9", (it.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })] }, `collage-${it.itemNumber}`));
|
|
63
|
+
].join(" "), "aria-label": `View ${it.title || `prize #${it.itemNumber}`} in lightbox`, children: [_jsxs(Div, { className: "relative aspect-square w-full", children: [cover ? (_jsx(MediaImage, { src: cover, alt: it.title || `Prize #${it.itemNumber}`, size: "thumbnail" })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No image" }) })), _jsxs(Div, { textWeight: "semibold", textSize: "xs", surface: "overlay-xl", padding: "chip-2xs", className: "absolute left-2 top-2 text-white", rounded: "default", children: ["#", it.itemNumber] }), it.isWon && !hideWonState ? (_jsxs(_Fragment, { children: [_jsx(Scrim, { direction: "diagonal", intensity: "medium", multiply: true, className: "absolute inset-0" }), _jsx(Row, { className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Text, { className: CLS_WON_STAMP, children: wonLabel }) })] })) : null] }), _jsxs(Div, { padding: "xs", children: [_jsx(Text, { className: "line-clamp-2", size: "sm", weight: "medium", children: it.title || `Prize #${it.itemNumber}` }), it.estimatedValue != null ? (_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["est. \u20B9", (it.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })] }, `collage-${it.itemNumber}`));
|
|
65
64
|
}) }), _jsx(ImageLightbox, { images: galleryImages, activeIndex: lightboxIndex, onClose: () => setLightboxIndex(null), onNavigate: (i) => setLightboxIndex(i), showThumbnails: true })] }));
|
|
66
65
|
}
|
|
67
66
|
export default PrizeDrawCollage;
|
|
@@ -18,4 +18,4 @@ export interface PrizeDrawEntryActionsProps {
|
|
|
18
18
|
* - On confirm → add 1 entry to guest cart and route to /user/cart
|
|
19
19
|
* - "View RNG source" link → prizeGithubFileUrl
|
|
20
20
|
*/
|
|
21
|
-
export declare function PrizeDrawEntryActions({ productId, productSlug, title, thumb, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, storeId, storeName, }: PrizeDrawEntryActionsProps): import("react").JSX.Element;
|
|
21
|
+
export declare function PrizeDrawEntryActions({ productId, productSlug, title, thumb: _thumb, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, storeId: _storeId, storeName: _storeName, }: PrizeDrawEntryActionsProps): import("react").JSX.Element;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import { Button, Stack, Text, LoginRequiredModal } from "../../../ui";
|
|
5
|
+
import { Anchor, Button, Stack, Text, LoginRequiredModal } from "../../../ui";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
7
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
8
8
|
import { NonRefundableConsentModal } from "./NonRefundableConsentModal";
|
|
@@ -16,7 +16,7 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
|
16
16
|
* - On confirm → add 1 entry to guest cart and route to /user/cart
|
|
17
17
|
* - "View RNG source" link → prizeGithubFileUrl
|
|
18
18
|
*/
|
|
19
|
-
export function PrizeDrawEntryActions({ productId, productSlug, title, thumb, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, storeId, storeName, }) {
|
|
19
|
+
export function PrizeDrawEntryActions({ productId, productSlug, title, thumb: _thumb, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, storeId: _storeId, storeName: _storeName, }) {
|
|
20
20
|
const router = useRouter();
|
|
21
21
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
22
22
|
const [consentOpen, setConsentOpen] = useState(false);
|
|
@@ -31,5 +31,5 @@ export function PrizeDrawEntryActions({ productId, productSlug, title, thumb, pr
|
|
|
31
31
|
const slug = productSlug ?? productId;
|
|
32
32
|
router.push(`${String(ROUTES.USER.CHECKOUT)}?directItem=${encodeURIComponent(slug)}&type=prize-draw`);
|
|
33
33
|
}, [productSlug, productId, router]);
|
|
34
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "2xl", weight: "bold", color: "primary", children: [formatCurrency(pricePerEntry, currency), _jsx(Text, { as: "span", className: "ml-1 text-[var(--appkit-color-text-muted)]", size: "xs", weight: "normal", children: "per entry" })] }), _jsx(Button, { action: closed ? undefined : ACTIONS.PRIZE_DRAW["buy-now"], variant: "primary", size: "md", className: "w-full", disabled: closed, onClick: handleEnter, children: closed ? "Draw closed" : undefined }), prizeGithubFileUrl ? (_jsx(
|
|
34
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "2xl", weight: "bold", color: "primary", children: [formatCurrency(pricePerEntry, currency), _jsx(Text, { as: "span", className: "ml-1 text-[var(--appkit-color-text-muted)]", size: "xs", weight: "normal", children: "per entry" })] }), _jsx(Button, { action: closed ? undefined : ACTIONS.PRIZE_DRAW["buy-now"], variant: "primary", size: "md", className: "w-full", disabled: closed, onClick: handleEnter, children: closed ? "Draw closed" : undefined }), prizeGithubFileUrl ? (_jsx(Anchor, { href: prizeGithubFileUrl, tone: "none", underline: "hover", size: "xs", className: "text-center font-medium text-primary-600 underline-offset-4 dark:text-primary-400", children: "View RNG source code on GitHub \u2192" })) : null, _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", align: "center", children: "Winners chosen by Node.js crypto.randomInt \u2014 fully auditable. Entries are locked once paid; refunds only if the prize pool is exhausted." }), _jsx(NonRefundableConsentModal, { open: consentOpen, listingType: "prize-draw", itemTitle: title, priceLabel: formatCurrency(pricePerEntry, currency), onCancel: () => setConsentOpen(false), onConfirm: handleConfirm }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
35
35
|
}
|
|
@@ -15,7 +15,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
* nor LetItRip admins can influence the outcome.
|
|
16
16
|
*/
|
|
17
17
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
18
|
-
import { Button, Div, Heading, LoginRequiredModal, Modal, Span, Stack, Text, useToast } from "../../../ui";
|
|
18
|
+
import { Anchor, Button, Div, Heading, LoginRequiredModal, Modal, Span, Stack, Text, useToast } from "../../../ui";
|
|
19
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
19
20
|
import { isAuthError } from "../../../utils/auth-error";
|
|
20
21
|
import { PrizeDrawCollage } from "./PrizeDrawCollage";
|
|
21
22
|
import { normalizeError } from "../../../errors/normalize";
|
|
@@ -149,8 +150,6 @@ export function PrizeRevealModal({ open, onClose, items, orderId, productId, onR
|
|
|
149
150
|
}, REVEAL_DURATION_MS);
|
|
150
151
|
}, [onReveal, orderId, phase, productId, startAnimation]);
|
|
151
152
|
const winnerImg = winner?.images?.[0];
|
|
152
|
-
return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: CLS_REFUND_NOTE, children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { size: "sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "border border-error/40", color: "error", surface: "danger-surface", padding: "inline", rounded: "default", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Something went wrong" }), _jsx(Text, { size: "sm", children: errorMessage })] })) : null, _jsx(PrizeDrawCollage, { items: items, hideWonState: true, highlightItemNumber: highlight }), phase === "idle" ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: handleRevealClick, children: "\u2728 Reveal my prize" })) : null, phase === "revealing" ? (_jsxs(Div, { className: `bg-[var(--appkit-color-surface-muted)] ${__P.p4} text-center`, rounded: "default", children: [_jsx(Text, { size: "lg", weight: "semibold", children: "Rolling\u2026" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "The winner was locked by the server before this animation started. Hang tight \u2014 we're just making it look pretty." })] })) : null, phase === "won" && winner ? (_jsxs(Div, { className: `border-2 border-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface)] ${__P.p4} text-center`, rounded: "lg", children: [_jsx(Text, { className: "tracking-wider text-[var(--appkit-color-text-muted)]", size: "xs", transform: "uppercase", children: "You won" }), _jsxs(Heading, { level: 2, className: "my-2", children: ["#", winner.itemNumber, " \u2014 ", winner.title] }), winnerImg ? (
|
|
153
|
-
/* eslint-disable-next-line @next/next/no-img-element */
|
|
154
|
-
_jsx("img", { src: winnerImg, alt: winner.title, className: "mx-auto max-h-64 rounded" })) : null, winner.estimatedValue != null ? (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Estimated value: \u20B9", (winner.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })) : null, _jsxs(Div, { textSize: "xs", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-muted)] text-[var(--appkit-color-text-muted)]", padding: "inlineSm", rounded: "default", children: [_jsx(Span, { weight: "bold", children: "Fairness guarantee:" }), " ", "Winners are picked by ", _jsx("code", { children: "crypto.randomInt" }), " running on LetItRip's server before the animation starts. The animation is theatrical \u2014 neither the store nor LetItRip staff can influence the outcome.", " ", effectiveRngUrl ? (_jsx("a", { href: effectiveRngUrl, target: "_blank", rel: "noreferrer noopener", className: "underline", children: "View RNG source code \u2192" })) : null] })] })] }));
|
|
153
|
+
return (_jsxs(Modal, { isOpen: open, onClose: onClose, title: "Prize Reveal", size: "lg", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to reveal your prize. Please log in or create an account to continue." }), _jsxs(Stack, { gap: "md", children: [phase === "refunded" ? (_jsxs(Div, { className: CLS_REFUND_NOTE, children: [_jsx(Heading, { level: 3, className: "mb-1", children: "Pool exhausted \u2014 you've been refunded" }), _jsx(Text, { size: "sm", children: "Every prize in this draw was already claimed by the time your entry rolled. Your order has been marked refunded automatically." })] })) : null, phase === "error" ? (_jsxs(Div, { className: "border border-error/40", color: "error", surface: "danger-surface", padding: "inline", rounded: "default", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Something went wrong" }), _jsx(Text, { size: "sm", children: errorMessage })] })) : null, _jsx(PrizeDrawCollage, { items: items, hideWonState: true, highlightItemNumber: highlight }), phase === "idle" ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: handleRevealClick, children: "\u2728 Reveal my prize" })) : null, phase === "revealing" ? (_jsxs(Div, { className: `bg-[var(--appkit-color-surface-muted)] ${__P.p4} text-center`, rounded: "default", children: [_jsx(Text, { size: "lg", weight: "semibold", children: "Rolling\u2026" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "The winner was locked by the server before this animation started. Hang tight \u2014 we're just making it look pretty." })] })) : null, phase === "won" && winner ? (_jsxs(Div, { className: `border-2 border-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface)] ${__P.p4} text-center`, rounded: "lg", children: [_jsx(Text, { className: "tracking-wider text-[var(--appkit-color-text-muted)]", size: "xs", transform: "uppercase", children: "You won" }), _jsxs(Heading, { level: 2, className: "my-2", children: ["#", winner.itemNumber, " \u2014 ", winner.title] }), winnerImg ? (_jsx(Div, { className: "relative mx-auto h-64 w-64 max-w-full overflow-hidden", rounded: "md", children: _jsx(MediaImage, { src: winnerImg, alt: winner.title, size: "hero", objectFit: "contain" }) })) : null, winner.estimatedValue != null ? (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Estimated value: \u20B9", (winner.estimatedValue / 100).toLocaleString("en-IN")] })) : null] })) : null, _jsxs(Div, { textSize: "xs", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-muted)] text-[var(--appkit-color-text-muted)]", padding: "inlineSm", rounded: "default", children: [_jsx(Span, { weight: "bold", children: "Fairness guarantee:" }), " ", "Winners are picked by ", _jsx("code", { children: "crypto.randomInt" }), " running on LetItRip's server before the animation starts. The animation is theatrical \u2014 neither the store nor LetItRip staff can influence the outcome.", " ", effectiveRngUrl ? (_jsx(Anchor, { href: effectiveRngUrl, tone: "none", underline: "always", children: "View RNG source code \u2192" })) : null] })] })] }));
|
|
155
154
|
}
|
|
156
155
|
export default PrizeRevealModal;
|
|
@@ -22,4 +22,4 @@ export interface ProductDetailActionsProps {
|
|
|
22
22
|
* to the server APIs with guest-fallback. Replaces the previously-unwired
|
|
23
23
|
* placeholder buttons inside ProductDetailPageView.
|
|
24
24
|
*/
|
|
25
|
-
export declare function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant, }: ProductDetailActionsProps): import("react").JSX.Element;
|
|
25
|
+
export declare function ProductDetailActions({ productId, productSlug: _productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant, }: ProductDetailActionsProps): import("react").JSX.Element;
|
|
@@ -18,7 +18,7 @@ import { normalizeError } from "../../../errors/normalize";
|
|
|
18
18
|
* to the server APIs with guest-fallback. Replaces the previously-unwired
|
|
19
19
|
* placeholder buttons inside ProductDetailPageView.
|
|
20
20
|
*/
|
|
21
|
-
export function ProductDetailActions({ productId, productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant = "desktop", }) {
|
|
21
|
+
export function ProductDetailActions({ productId, productSlug: _productSlug, productTitle, productImage, price, currency, storeId, storeName, inStock, variant = "desktop", }) {
|
|
22
22
|
const router = useRouter();
|
|
23
23
|
const { showToast } = useToast();
|
|
24
24
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
@@ -44,5 +44,7 @@ export interface ProductDetailPageViewProps {
|
|
|
44
44
|
productFeatures?: ProductFeatureDocument[];
|
|
45
45
|
/** Site-wide COD availability (`siteSettings.payment.codEnabled`), threaded from the page's data layer. Drives the "Cash on Delivery" feature badge. */
|
|
46
46
|
codEnabled?: boolean;
|
|
47
|
+
/** Site-wide EMI availability (`siteSettings.emi.enabled`), threaded from the page's data layer. Drives the "EMI Available" feature badge. */
|
|
48
|
+
emiEnabled?: boolean;
|
|
47
49
|
}
|
|
48
|
-
export declare function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, }: ProductDetailPageViewProps): Promise<import("react").JSX.Element>;
|
|
50
|
+
export declare function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, emiEnabled, }: ProductDetailPageViewProps): Promise<import("react").JSX.Element>;
|
|
@@ -106,7 +106,7 @@ function StarRating({ value }) {
|
|
|
106
106
|
// ---------------------------------------------------------------------------
|
|
107
107
|
// Component
|
|
108
108
|
// ---------------------------------------------------------------------------
|
|
109
|
-
export async function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, }) {
|
|
109
|
+
export async function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, emiEnabled, }) {
|
|
110
110
|
// Use pre-fetched data when available to avoid a redundant repository call.
|
|
111
111
|
// The page layer wraps getProductForDetail in React.cache(), so both
|
|
112
112
|
// generateMetadata() and this component share the same in-flight promise.
|
|
@@ -262,7 +262,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
262
262
|
? ` — only ${effectiveStock} left`
|
|
263
263
|
: ""] })] })) : (_jsxs(Span, { layout: "inline-flex", gap: "xs", className: `w-fit .5 ${inStock ? CLS_STOCK_IN : CLS_STOCK_OUT}`, rounded: "full", padding: "pill-md", size: "xs", weight: "medium", children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
|
|
264
264
|
? ` — only ${effectiveStock} left`
|
|
265
|
-
: ""] })), (categorySlugs.length > 0 || subcategory || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [categorySlugs.map((slug, i) => (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(slug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryNames[i] ?? slug }, slug))), subcategory && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", transform: "capitalize", children: subcategory })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand }))] })), bundleMemberships.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, children: bundleMemberships.map((b) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.BUNDLE_DETAIL(b.id)), className: CLS_BUNDLE_PILL, children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), "In bundle: ", b.title] }, b.id))) })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, codAvailable: codEnabled === true, condition: condition ?? undefined, returnable: returnPolicy != null && returnPolicy.length > 0, labels: {
|
|
265
|
+
: ""] })), (categorySlugs.length > 0 || subcategory || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [categorySlugs.map((slug, i) => (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(slug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryNames[i] ?? slug }, slug))), subcategory && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", transform: "capitalize", children: subcategory })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand }))] })), bundleMemberships.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, children: bundleMemberships.map((b) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.BUNDLE_DETAIL(b.id)), className: CLS_BUNDLE_PILL, children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), "In bundle: ", b.title] }, b.id))) })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, codAvailable: codEnabled === true, codHref: String(ROUTES.PUBLIC.HOW_CHECKOUT_WORKS), emiAvailable: emiEnabled === true, emiHref: String(ROUTES.PUBLIC.HOW_EMI_WORKS), condition: condition ?? undefined, returnable: returnPolicy != null && returnPolicy.length > 0, labels: {
|
|
266
266
|
featured: "Featured",
|
|
267
267
|
fasterDelivery: "Faster Delivery",
|
|
268
268
|
ratedSeller: "Rated Seller",
|
|
@@ -274,6 +274,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
|
|
|
274
274
|
returnable: "Returnable",
|
|
275
275
|
freeShipping: "Free Shipping",
|
|
276
276
|
codAvailable: "Cash on Delivery",
|
|
277
|
+
emiAvailable: "EMI Available",
|
|
277
278
|
wishlistCount: (n) => `${n} wishlisted`,
|
|
278
279
|
categoryProductCount: (n, cat) => `${n} in ${cat}`,
|
|
279
280
|
} }), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this product" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => (_jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller }), typeof p.storeRating === "number" && p.storeRating > 0 && (_jsx(Row, { gap: "xs", align: "center", className: "mt-0.5", children: _jsxs(Text, { size: "xs", color: "muted", children: [_jsx(Span, { className: "text-warning", "aria-hidden": "true", children: "\u2605" }), " ", p.storeRating.toFixed(1), typeof p.storeReviewCount === "number" &&
|
|
@@ -10,6 +10,7 @@ interface ProductFeatureBadgeLabels {
|
|
|
10
10
|
returnable: string;
|
|
11
11
|
freeShipping: string;
|
|
12
12
|
codAvailable: string;
|
|
13
|
+
emiAvailable: string;
|
|
13
14
|
wishlistCount: (count: number) => string;
|
|
14
15
|
categoryProductCount: (count: string, category: string) => string;
|
|
15
16
|
}
|
|
@@ -24,9 +25,14 @@ interface ProductFeatureBadgesProps {
|
|
|
24
25
|
categoryProductCount?: number;
|
|
25
26
|
categoryName?: string;
|
|
26
27
|
codAvailable?: boolean;
|
|
28
|
+
/** When set, the COD badge links here (e.g. the "How Checkout Works" policy page). */
|
|
29
|
+
codHref?: string;
|
|
30
|
+
emiAvailable?: boolean;
|
|
31
|
+
/** When set, the EMI badge links here (e.g. the "How EMI Works" policy page). */
|
|
32
|
+
emiHref?: string;
|
|
27
33
|
labels: ProductFeatureBadgeLabels;
|
|
28
34
|
formatCount?: (value: number) => string;
|
|
29
35
|
categoryBadgeClassName?: string;
|
|
30
36
|
}
|
|
31
|
-
export declare function ProductFeatureBadges({ featured, fasterDelivery, ratedSeller, condition, returnable, freeShipping, wishlistCount, categoryProductCount, categoryName, codAvailable, labels, formatCount, categoryBadgeClassName, }: ProductFeatureBadgesProps): import("react").JSX.Element | null;
|
|
37
|
+
export declare function ProductFeatureBadges({ featured, fasterDelivery, ratedSeller, condition, returnable, freeShipping, wishlistCount, categoryProductCount, categoryName, codAvailable, codHref, emiAvailable, emiHref, labels, formatCount, categoryBadgeClassName, }: ProductFeatureBadgesProps): import("react").JSX.Element | null;
|
|
32
38
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { Row, Span, TextLink } from "../../../ui";
|
|
4
|
+
export function ProductFeatureBadges({ featured, fasterDelivery, ratedSeller, condition, returnable, freeShipping, wishlistCount, categoryProductCount, categoryName, codAvailable, codHref, emiAvailable, emiHref, labels, formatCount, categoryBadgeClassName, }) {
|
|
4
5
|
const badges = [];
|
|
5
6
|
if (featured) {
|
|
6
7
|
badges.push({
|
|
@@ -72,6 +73,17 @@ export function ProductFeatureBadges({ featured, fasterDelivery, ratedSeller, co
|
|
|
72
73
|
label: labels.codAvailable,
|
|
73
74
|
colorClass: "text-purple-700 dark:text-purple-300",
|
|
74
75
|
bgClass: "bg-purple-50 dark:bg-purple-900/30 border-purple-200 dark:border-purple-800",
|
|
76
|
+
href: codHref,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (emiAvailable) {
|
|
80
|
+
badges.push({
|
|
81
|
+
key: "emi",
|
|
82
|
+
icon: _jsx(Span, { size: "xs", children: "\uD83D\uDCC5" }),
|
|
83
|
+
label: labels.emiAvailable,
|
|
84
|
+
colorClass: "text-indigo-700 dark:text-indigo-300",
|
|
85
|
+
bgClass: "bg-indigo-50 dark:bg-indigo-900/30 border-indigo-200 dark:border-indigo-800",
|
|
86
|
+
href: emiHref,
|
|
75
87
|
});
|
|
76
88
|
}
|
|
77
89
|
if (wishlistCount && wishlistCount > 0) {
|
|
@@ -98,5 +110,6 @@ export function ProductFeatureBadges({ featured, fasterDelivery, ratedSeller, co
|
|
|
98
110
|
}
|
|
99
111
|
if (badges.length === 0)
|
|
100
112
|
return null;
|
|
101
|
-
|
|
113
|
+
const renderPill = (badge) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", className: `.5 border py-[0.375rem] ${badge.bgClass} ${badge.colorClass}`, rounded: "lg", padding: "x-sm", size: "xs", weight: "medium", children: [_jsx(Span, { "aria-hidden": "true", children: badge.icon }), badge.label] }));
|
|
114
|
+
return (_jsx(Row, { wrap: true, gap: "sm", children: badges.map((badge) => badge.href ? (_jsx(TextLink, { href: badge.href, className: "no-underline", children: renderPill(badge) }, badge.key)) : (_jsx(Fragment, { children: renderPill(badge) }, badge.key))) }));
|
|
102
115
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Div, Heading, Row, Stack, Text } from "../../../ui";
|
|
3
|
+
import { Anchor, Div, Heading, Row, Stack, Text } from "../../../ui";
|
|
4
4
|
import { Badge } from "../../../ui/components/Badge";
|
|
5
5
|
import { MediaImage } from "../../media/MediaImage";
|
|
6
6
|
const __O = {
|
|
@@ -25,5 +25,5 @@ function buildCertLookupUrl(service, certNumber) {
|
|
|
25
25
|
*/
|
|
26
26
|
export function ProductGradingTab({ grading, slabImageUrl }) {
|
|
27
27
|
const lookupUrl = buildCertLookupUrl(grading.service, grading.certNumber);
|
|
28
|
-
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { align: "center", gap: "md", children: [_jsxs(Stack, { justify: "center", className: "border-2 border-[var(--appkit-color-primary)] min-w-[120px]", padding: "inlineLg", align: "center", rounded: "2xl", children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "bold", transform: "uppercase", children: grading.service }), _jsx(Text, { className: "font-extrabold", size: "4xl", children: grading.grade })] }), _jsxs(Stack, { gap: "xs", className: "flex-1", children: [_jsxs(Heading, { level: 3, size: "base", weight: "semibold", children: ["Graded by ", grading.service] }), grading.certNumber ? (_jsxs(Text, { size: "sm", color: "muted", children: ["Cert #", grading.certNumber, lookupUrl ? (_jsxs(_Fragment, { children: [" — ", _jsx(
|
|
28
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { align: "center", gap: "md", children: [_jsxs(Stack, { justify: "center", className: "border-2 border-[var(--appkit-color-primary)] min-w-[120px]", padding: "inlineLg", align: "center", rounded: "2xl", children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "bold", transform: "uppercase", children: grading.service }), _jsx(Text, { className: "font-extrabold", size: "4xl", children: grading.grade })] }), _jsxs(Stack, { gap: "xs", className: "flex-1", children: [_jsxs(Heading, { level: 3, size: "base", weight: "semibold", children: ["Graded by ", grading.service] }), grading.certNumber ? (_jsxs(Text, { size: "sm", color: "muted", children: ["Cert #", grading.certNumber, lookupUrl ? (_jsxs(_Fragment, { children: [" — ", _jsx(Anchor, { href: lookupUrl, tone: "none", underline: "hover", className: "text-[var(--appkit-color-primary)]", children: "Verify" })] })) : null] })) : null] })] }), slabImageUrl ? (_jsx(Div, { className: `${__O.hidden} aspect-[4/3] w-full`, rounded: "2xl", border: "default", children: _jsx(MediaImage, { src: slabImageUrl, alt: "Slab certificate image", size: "gallery", objectFit: "contain" }) })) : null, grading.attributes && Object.keys(grading.attributes).length > 0 ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 3, className: "tracking-wide", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: "Subgrades & attributes" }), _jsx(Row, { wrap: true, gap: "sm", children: Object.entries(grading.attributes).map(([key, value]) => (_jsxs(Badge, { variant: "default", children: [key, ": ", value] }, key))) })] })) : null] }));
|
|
29
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { productRepository } from "../../../repositories";
|
|
3
3
|
import { ROUTES } from "../../../constants";
|
|
4
|
-
import { Container, Div, Heading, Main, Section, Text } from "../../../ui";
|
|
4
|
+
import { Container, Div, Heading, Main, Section, Text, TextLink } from "../../../ui";
|
|
5
5
|
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
6
6
|
import { parseListingSearchParams } from "../../../utils/listing-params";
|
|
7
7
|
import { ProductsIndexListing } from "./ProductsIndexListing";
|
|
@@ -59,5 +59,5 @@ export async function ProductsIndexPageView({ searchParams = {} }) {
|
|
|
59
59
|
})
|
|
60
60
|
.catch(() => null);
|
|
61
61
|
const products = result ?? null;
|
|
62
|
-
return (_jsxs(Main, { children: [_jsx(Section, { border: "bottom-subtle", paddingY: "b-md", padding: "t-xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, size: "3xl", weight: "bold", color: "primary", children: "Products" }), _jsx(Text, { className: "mt-1", color: "muted", size: "sm", children: "Discover amazing products and deals" }), _jsx(Div, { className: "mt-3", children: _jsx("
|
|
62
|
+
return (_jsxs(Main, { children: [_jsx(Section, { border: "bottom-subtle", paddingY: "b-md", padding: "t-xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, size: "3xl", weight: "bold", color: "primary", children: "Products" }), _jsx(Text, { className: "mt-1", color: "muted", size: "sm", children: "Discover amazing products and deals" }), _jsx(Div, { className: "mt-3", children: _jsx(TextLink, { variant: "bare", href: String(ROUTES.PUBLIC.AUCTIONS), rounded: "full", paddingX: "sm", size: "xs", weight: "medium", layout: "inline-flex", align: "center", gap: "sm", className: "border border-primary/30 bg-primary/10 py-1.5 text-primary-700 dark:text-primary-400 hover:bg-primary/15 transition-colors", children: "\uD83C\uDFF7\uFE0F Looking for unique deals? Browse Auctions \u2192" }) })] }) }), _jsxs(Container, { size: "xl", padding: "x-md", children: [_jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-4 mt-4" }), _jsx(ProductsIndexListing, { initialData: products }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] })] }));
|
|
63
63
|
}
|
|
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
|
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { ROUTES } from "../../../next";
|
|
6
6
|
import { Div, Row, Span, Text } from "../../../ui";
|
|
7
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
7
8
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
8
9
|
import { normalizeListingType } from "../utils/listing-type";
|
|
9
10
|
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
@@ -19,11 +20,9 @@ function ListingThumb({ listing, isCurrent, }) {
|
|
|
19
20
|
const image = listing.images?.[0] ?? listing.mainImage ?? "";
|
|
20
21
|
const href = getHref(listing);
|
|
21
22
|
const price = formatCurrency(listing.price, listing.currency ?? "INR");
|
|
22
|
-
return (_jsxs(Link, { href: href, "aria-label": listing.title, className: "flex flex-col items-center gap-1.5 flex-shrink-0 w-16 group", children: [_jsx(Div, { rounded: "full", className: `w-14 h-14 overflow-hidden border-2 transition-all ${isCurrent
|
|
23
|
+
return (_jsxs(Link, { href: href, "aria-label": listing.title, className: "flex flex-col items-center gap-1.5 flex-shrink-0 w-16 group", children: [_jsx(Div, { rounded: "full", className: `relative w-14 h-14 overflow-hidden border-2 transition-all ${isCurrent
|
|
23
24
|
? "border-[var(--appkit-color-primary)] ring-2 ring-[var(--appkit-color-primary)]/30"
|
|
24
|
-
: "border-[var(--appkit-color-border)] group-hover:border-[var(--appkit-color-primary)]"}`, children: image ? (
|
|
25
|
-
// eslint-disable-next-line @next/next/no-img-element
|
|
26
|
-
_jsx("img", { src: image, alt: listing.title, className: "w-full h-full object-cover", loading: "lazy" })) : (_jsx(Row, { textSize: "xs", color: "faint", className: "w-full h-full", surface: "subtle", align: "center", justify: "center", children: "\u00E2\u2014\u00AF" })) }), _jsx(Text, { className: "text-[10px] leading-tight line-clamp-2 w-full", color: "muted", align: "center", children: listing.title }), _jsx(Text, { className: "text-[10px]", color: "primary", weight: "semibold", children: price })] }));
|
|
25
|
+
: "border-[var(--appkit-color-border)] group-hover:border-[var(--appkit-color-primary)]"}`, children: image ? (_jsx(MediaImage, { src: image, alt: listing.title, size: "avatar" })) : (_jsx(Row, { textSize: "xs", color: "faint", className: "w-full h-full", surface: "subtle", align: "center", justify: "center", children: "\u00E2\u2014\u00AF" })) }), _jsx(Text, { className: "text-[10px] leading-tight line-clamp-2 w-full", color: "muted", align: "center", children: listing.title }), _jsx(Text, { className: "text-[10px]", color: "primary", weight: "semibold", children: price })] }));
|
|
27
26
|
}
|
|
28
27
|
export function SublistingCarouselSection({ sublistingCategoryId, currentListingId }) {
|
|
29
28
|
const [open, setOpen] = useState(false);
|
|
@@ -7,4 +7,4 @@ export interface CouponsIndexListingProps {
|
|
|
7
7
|
/** If set, only show coupons from this store (id, for filtering) */
|
|
8
8
|
storeId?: string;
|
|
9
9
|
}
|
|
10
|
-
export declare function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }: CouponsIndexListingProps): React.JSX.Element;
|
|
10
|
+
export declare function CouponsIndexListing({ initialCoupons, storeSlug: _storeSlug, storeId, }: CouponsIndexListingProps): React.JSX.Element;
|
|
@@ -28,7 +28,7 @@ const COUPON_TYPES = [
|
|
|
28
28
|
{ value: COUPON_FIELDS.TYPE_VALUES.BUY_X_GET_Y, label: "Buy X Get Y" },
|
|
29
29
|
];
|
|
30
30
|
const FILTER_KEYS = [TABLE_KEYS.TYPE, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
|
|
31
|
-
export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
|
|
31
|
+
export function CouponsIndexListing({ initialCoupons, storeSlug: _storeSlug, storeId, }) {
|
|
32
32
|
const table = useUrlTable({ defaults: { pageSize: "12", sort: DEFAULT_SORT } });
|
|
33
33
|
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
34
34
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -19,6 +19,7 @@ const CLS_ICON_PURPLE = "flex h-10 w-10 flex-shrink-0 items-center justify-cente
|
|
|
19
19
|
const CLS_ICON_PURPLE_BARE = "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-purple-100 dark:bg-purple-900/30";
|
|
20
20
|
import Link from "next/link";
|
|
21
21
|
import { Div, Grid, Heading, RichText, Row, Section, Span, StarRating, Stack, Text } from "../../../ui";
|
|
22
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
22
23
|
import { maskName } from "../../../security";
|
|
23
24
|
import { getDefaultLocale } from "../../../core/baseline-resolver";
|
|
24
25
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
@@ -91,9 +92,9 @@ export function ReviewDetailShell({ review, storeHref }) {
|
|
|
91
92
|
? String(ROUTES.PUBLIC.PROFILE(reviewerProfileId))
|
|
92
93
|
: null;
|
|
93
94
|
const currentImage = lightboxIdx !== null ? images[lightboxIdx] : null;
|
|
94
|
-
return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "default", className: "border-b border-neutral-200 pt-[2.5rem]", padding: "b-xl", children: _jsxs(Div, { className: "mx-auto max-w-3xl", padding: "x-md", children: [_jsxs(Row, { gap: "sm", className: "mb-4", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs(Span, { color: "inverse", weight: "bold", className: "text-[var(--appkit-color-text)] dark:", size: "2xl", children: [review.rating, ".0"] }), review.verified && (_jsx(Span, { layout: "inline-flex", gap: "xs", color: "success", surface: "success-surface", size: "xs", weight: "semibold", rounded: "full", padding: "pill-md", children: "\u2713 Verified Purchase" })), review.featured && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_RATING_PILL, children: "\u2605 Featured" }))] }), review.title && (_jsx(Heading, { color: "inverse", level: 1, className: "text-[var(--appkit-color-text)] dark: mb-4 leading-snug", size: "2xl", weight: "bold", children: review.title })), _jsxs(Row, { gap: "sm", children: [review.userAvatar ? (_jsx(
|
|
95
|
+
return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "default", className: "border-b border-neutral-200 pt-[2.5rem]", padding: "b-xl", children: _jsxs(Div, { className: "mx-auto max-w-3xl", padding: "x-md", children: [_jsxs(Row, { gap: "sm", className: "mb-4", children: [_jsx(StarRating, { value: review.rating, size: "lg", readOnly: true }), _jsxs(Span, { color: "inverse", weight: "bold", className: "text-[var(--appkit-color-text)] dark:", size: "2xl", children: [review.rating, ".0"] }), review.verified && (_jsx(Span, { layout: "inline-flex", gap: "xs", color: "success", surface: "success-surface", size: "xs", weight: "semibold", rounded: "full", padding: "pill-md", children: "\u2713 Verified Purchase" })), review.featured && (_jsx(Span, { size: "xs", weight: "semibold", className: CLS_RATING_PILL, children: "\u2605 Featured" }))] }), review.title && (_jsx(Heading, { color: "inverse", level: 1, className: "text-[var(--appkit-color-text)] dark: mb-4 leading-snug", size: "2xl", weight: "bold", children: review.title })), _jsxs(Row, { gap: "sm", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-11 w-11 flex-shrink-0 overflow-hidden ring-2 ring-white ring-[var(--appkit-color-border-subtle)]", rounded: "full", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "bold", textSize: "base", centered: true, className: "h-11 w-11 flex-shrink-0 bg-primary/10 text-primary ring-2 ring-white ring-[var(--appkit-color-border-subtle)]", rounded: "full", children: initials })), _jsxs(Div, { className: "min-w-0", children: [reviewerHref ? (_jsx(Link, { href: reviewerHref, className: "text-sm font-semibold text-[var(--appkit-color-text)] dark:text-white hover:text-primary transition-colors", children: displayName })) : (_jsx(Span, { color: "inverse", size: "sm", weight: "semibold", className: "text-[var(--appkit-color-text)] dark:", children: review.isAnonymous ? "Anonymous" : displayName })), date && (_jsx(Text, { size: "xs", color: "muted", className: "mt-0.5", children: date }))] })] })] }) }), _jsxs(Stack, { gap: "xl", className: "mx-auto max-w-3xl", paddingY: "y-xl", paddingX: "x-md", children: [review.comment && (_jsx(Section, { children: _jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-neutral dark:prose-invert max-w-none prose-p:leading-relaxed prose-headings:font-semibold prose-img:rounded-lg prose-a:text-primary", className: "text-neutral-700 text-[var(--appkit-color-text-muted)]" }) })), images.length > 0 && (_jsxs(Section, { children: [_jsxs(Heading, { level: 2, className: "tracking-wide mb-3", color: "faint", size: "sm", weight: "semibold", transform: "uppercase", children: ["Photos (", images.length, ")"] }), _jsx(Grid, { gap: "xs", className: "grid-cols-3 sm:grid-cols-4", children: images.map((img, i) => (_jsxs("button", { type: "button", onClick: () => setLightboxIdx(i), "aria-label": `View photo ${i + 1}`, className: "group relative aspect-square overflow-hidden rounded-xl border border-neutral-200 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)] focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsx(MediaImage, { src: img.thumbnailUrl ?? img.url, alt: `Review image ${i + 1}`, size: "card", className: "transition-transform duration-300 group-hover:scale-105" }), _jsx(Row, { centered: true, className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity bg-[rgba(0,0,0,0.3)]", children: _jsx(Span, { color: "inverse", size: "xl", children: "\uD83D\uDD0D" }) })] }, i))) })] })), review.video && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "tracking-wide mb-3", color: "faint", size: "sm", weight: "semibold", transform: "uppercase", children: "Video" }), _jsx(Div, { className: `${__O.hidden} border border-[var(--appkit-color-border)] bg-[rgb(0,0,0)] aspect-video`, rounded: "xl", children: _jsx("video", { src: review.video.url, poster: review.video.thumbnailUrl, controls: true, className: "h-full w-full", preload: "metadata" }) })] })), _jsxs(Section, { className: "flex items-[center] gap-[1rem] border-t border-neutral-100", padding: "y-md", children: [_jsx(Div, { textSize: "sm", className: "text-[var(--appkit-color-text-muted)]", children: helpfulCount > 0 && (_jsxs(Span, { children: [_jsx(Span, { color: "inverse", weight: "bold", className: "text-[var(--appkit-color-text)] dark:", children: helpfulCount }), " ", helpfulCount === 1 ? "person" : "people", " found this helpful"] })) }), _jsxs("button", { type: "button", onClick: handleVote, disabled: voted || voting, className: `ml-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors ${voted
|
|
95
96
|
? CLS_HELPFUL_ACTIVE
|
|
96
|
-
: "border-neutral-300 border-[var(--appkit-color-border)] text-neutral-700 text-[var(--appkit-color-text-muted)] hover:border-primary hover:text-primary dark:hover:border-primary dark:hover:text-primary disabled:opacity-50"}`, children: [_jsx(Span, { "aria-hidden": "true", children: voted ? "✓" : "👍" }), voted ? "Marked helpful" : voting ? "Saving…" : "Helpful?"] })] }), _jsxs(Section, { className: "grid gap-[0.75rem] sm:grid-cols-3", children: [productHref && (_jsxs(Link, { href: productHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_ORANGE, children: "\uD83D\uDCE6" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Product" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: review.productTitle ?? "View Product" })] })] })), sellerHref && (_jsxs(Link, { href: sellerHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_BLUE, children: "\uD83C\uDFEA" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Seller" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: "View Seller" })] })] })), reviewerHref ? (_jsxs(Link, { href: reviewerHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_PURPLE, children: "\uD83D\uDC64" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Reviewer" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: displayName })] })] })) : (_jsxs(Row, { surface: "default", gap: "sm", className: `border border-neutral-200 dark:border-[var(--appkit-color-border)] ${__P.p4}`, rounded: "xl", children: [_jsx(Span, { size: "xl", className: CLS_ICON_PURPLE_BARE, children: "\uD83D\uDC64" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Span, { size: "xs", className: "block mb-0.5", color: "faint", children: "Reviewer" }), _jsx(Span, { color: "inverse", size: "sm", weight: "medium", className: "block text-[var(--appkit-color-text)] dark: truncate", children: "Anonymous" })] })] }))] })] }), lightboxIdx !== null && currentImage && (_jsxs(Row, { centered: true, className: "fixed inset-0 z-50 bg-[rgba(0,0,0,0.95)]", onClick: closeLightbox, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: [_jsx("button", { type: "button", onClick: closeLightbox, "aria-label": "Close lightbox", className: "absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/20 transition-colors text-xl", children: "\u00D7" }), _jsxs(Div, { textSize: "sm", className: "absolute top-4 left-1/2 -translate-x-1/2 text-white/70", children: [lightboxIdx + 1, " / ", images.length] }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); prevImage(); }, "aria-label": "Previous image", className: "absolute left-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u2039" })), _jsx(Row, { centered: true, className: "
|
|
97
|
+
: "border-neutral-300 border-[var(--appkit-color-border)] text-neutral-700 text-[var(--appkit-color-text-muted)] hover:border-primary hover:text-primary dark:hover:border-primary dark:hover:text-primary disabled:opacity-50"}`, children: [_jsx(Span, { "aria-hidden": "true", children: voted ? "✓" : "👍" }), voted ? "Marked helpful" : voting ? "Saving…" : "Helpful?"] })] }), _jsxs(Section, { className: "grid gap-[0.75rem] sm:grid-cols-3", children: [productHref && (_jsxs(Link, { href: productHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_ORANGE, children: "\uD83D\uDCE6" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Product" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: review.productTitle ?? "View Product" })] })] })), sellerHref && (_jsxs(Link, { href: sellerHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_BLUE, children: "\uD83C\uDFEA" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Seller" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: "View Seller" })] })] })), reviewerHref ? (_jsxs(Link, { href: reviewerHref, className: CLS_RELATED_LINK, children: [_jsx(Span, { className: CLS_ICON_PURPLE, children: "\uD83D\uDC64" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: CLS_RELATED_LABEL, children: "Reviewer" }), _jsx(Text, { className: CLS_RELATED_TITLE, children: displayName })] })] })) : (_jsxs(Row, { surface: "default", gap: "sm", className: `border border-neutral-200 dark:border-[var(--appkit-color-border)] ${__P.p4}`, rounded: "xl", children: [_jsx(Span, { size: "xl", className: CLS_ICON_PURPLE_BARE, children: "\uD83D\uDC64" }), _jsxs(Div, { className: "min-w-0", children: [_jsx(Span, { size: "xs", className: "block mb-0.5", color: "faint", children: "Reviewer" }), _jsx(Span, { color: "inverse", size: "sm", weight: "medium", className: "block text-[var(--appkit-color-text)] dark: truncate", children: "Anonymous" })] })] }))] })] }), lightboxIdx !== null && currentImage && (_jsxs(Row, { centered: true, className: "fixed inset-0 z-50 bg-[rgba(0,0,0,0.95)]", onClick: closeLightbox, role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", children: [_jsx("button", { type: "button", onClick: closeLightbox, "aria-label": "Close lightbox", className: "absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/20 transition-colors text-xl", children: "\u00D7" }), _jsxs(Div, { textSize: "sm", className: "absolute top-4 left-1/2 -translate-x-1/2 text-white/70", children: [lightboxIdx + 1, " / ", images.length] }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); prevImage(); }, "aria-label": "Previous image", className: "absolute left-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u2039" })), _jsx(Row, { centered: true, className: "relative h-[85vh] w-[85vw]", onClick: (e) => e.stopPropagation(), children: _jsx(MediaImage, { src: currentImage.url, alt: `Review photo ${lightboxIdx + 1}`, size: "hero", objectFit: "contain", className: "rounded-lg shadow-2xl" }) }), images.length > 1 && (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); nextImage(); }, "aria-label": "Next image", className: "absolute right-4 top-1/2 -translate-y-1/2 z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 text-white hover:bg-white/25 transition-colors text-2xl", children: "\u203A" })), images.length > 1 && (_jsx(Row, { justify: "center", gap: "xs", className: "absolute bottom-4 left-0 right-0", padding: "x-md", children: images.map((img, i) => (_jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); setLightboxIdx(i); }, "aria-label": `Go to image ${i + 1}`, className: `h-12 w-12 flex-shrink-0 rounded-md overflow-hidden border-2 transition-all ${i === lightboxIdx
|
|
97
98
|
? "border-white scale-110"
|
|
98
|
-
: "border-transparent opacity-60 hover:opacity-100"}`, children: _jsx(
|
|
99
|
+
: "border-transparent opacity-60 hover:opacity-100"}`, children: _jsx(MediaImage, { src: img.thumbnailUrl ?? img.url, alt: `Thumbnail ${i + 1}`, size: "thumbnail" }) }, i))) }))] }))] }));
|
|
99
100
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { Button, Div, Heading, Modal, RichText, Row, Stack, Text } from "../../../ui";
|
|
5
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
5
6
|
import { StarRating } from "../../../ui";
|
|
6
7
|
import { maskName } from "../../../security";
|
|
7
8
|
import { THEMED_TEXT_SUCCESS } from "../../../_internal/shared/styles/themed";
|
|
@@ -23,5 +24,5 @@ export function ViewReviewModal({ review, isOpen, onClose, }) {
|
|
|
23
24
|
day: "numeric",
|
|
24
25
|
})
|
|
25
26
|
: "";
|
|
26
|
-
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx(
|
|
27
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-10 w-10 flex-shrink-0 overflow-hidden", rounded: "full", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-10 w-10 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: displayName.charAt(0).toUpperCase() })), _jsxs(Div, { children: [_jsxs(Row, { gap: "sm", children: [_jsx(Text, { color: "inverse", className: "text-[var(--appkit-color-text)] dark:", weight: "medium", children: displayName }), review.verified && (_jsx(Text, { className: THEMED_TEXT_SUCCESS, size: "xs", children: "\u2713 Verified purchase" }))] }), _jsxs(Row, { className: "mt-0.5", gap: "sm", children: [_jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }), date && (_jsx(Text, { size: "xs", color: "faint", children: date }))] })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: review.images.map((img, i) => (_jsx("button", { type: "button", "aria-label": `View image ${i + 1}`, onClick: () => setLightboxIdx(i), className: `h-20 w-20 rounded-lg border overflow-hidden transition hover:opacity-80 p-0 flex-shrink-0 ${lightboxIdx === i ? "ring-2 ring-primary-500" : "border-neutral-100 border-[var(--appkit-color-border)]"}`, children: _jsx(MediaImage, { src: img.thumbnailUrl ?? img.url, alt: `Review thumbnail ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { size: "xs", color: "faint", children: [review.helpfulCount, " people found this helpful"] })), _jsx(Row, { justify: "end", padding: "t-xs", children: _jsx(Button, { variant: "ghost", onClick: onClose, children: "Close" }) })] }) }));
|
|
27
28
|
}
|
|
@@ -92,7 +92,7 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
92
92
|
const commitSearch = useCallback(() => {
|
|
93
93
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
94
94
|
}, [searchInput, table]);
|
|
95
|
-
const { reviews,
|
|
95
|
+
const { reviews, totalPages, isLoading } = useReviews({
|
|
96
96
|
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
97
97
|
rating: table.get(TABLE_KEYS.RATING) || undefined,
|
|
98
98
|
dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
|