@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
|
@@ -28,22 +28,43 @@ import { TitleBar } from "./TitleBar";
|
|
|
28
28
|
import { BackToTop } from "./BackToTop";
|
|
29
29
|
import { useDashboardNav } from "./DashboardNavContext";
|
|
30
30
|
import { usePathname } from "next/navigation";
|
|
31
|
+
import { findActiveNavItem } from "../../_internal/client/features/layout/navActive";
|
|
31
32
|
import { OVERLAY_FALLBACK_COLOR, SEED_DARK_BG as DEFAULT_DARK_BG, SEED_LIGHT_BG as DEFAULT_LIGHT_BG } from "./background-seed-defaults";
|
|
32
33
|
const CLS_STAT_BOX = "flex flex-col items-center gap-[var(--appkit-space-1)] p-[var(--appkit-space-2)] bg-[var(--appkit-color-surface)] rounded-lg text-center";
|
|
33
34
|
const CLS_STAT_LABEL = "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]";
|
|
34
35
|
const CLS_LOGOUT_BTN = "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-error transition-colors hover:bg-error-surface hover:text-error dark:text-error dark:hover:bg-error-surface dark:hover:text-error";
|
|
35
36
|
/** Collapsible accordion section for the public sidebar. */
|
|
36
|
-
function CollapsibleNavGroup({ title, children, }) {
|
|
37
|
-
const [open, setOpen] = useState(
|
|
37
|
+
function CollapsibleNavGroup({ title, hasActive = false, children, }) {
|
|
38
|
+
const [open, setOpen] = useState(hasActive);
|
|
39
|
+
// Re-sync whenever the route changes — the sidebar stays mounted across
|
|
40
|
+
// client-side navigation, so the lazy initial state above only fires once.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (hasActive)
|
|
43
|
+
setOpen(true);
|
|
44
|
+
}, [hasActive]);
|
|
38
45
|
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && _jsx(Stack, { gap: "none", className: "", children: children })] }));
|
|
39
46
|
}
|
|
40
|
-
function CollapsibleSidebarSection({ section, navItemClass, }) {
|
|
41
|
-
const
|
|
47
|
+
function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass, activeHref, }) {
|
|
48
|
+
const activeItem = findActiveNavItem(section.items, activeHref);
|
|
49
|
+
const hasActive = !!activeItem;
|
|
50
|
+
const [open, setOpen] = useState((section.defaultOpen ?? false) || hasActive);
|
|
51
|
+
// Re-sync whenever the route changes — the sidebar stays mounted across
|
|
52
|
+
// client-side navigation, so the lazy initial state above only fires once.
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (hasActive)
|
|
55
|
+
setOpen(true);
|
|
56
|
+
}, [hasActive]);
|
|
42
57
|
const hasTitle = !!section.title;
|
|
43
58
|
if (!hasTitle) {
|
|
44
|
-
return (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) =>
|
|
59
|
+
return (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) => {
|
|
60
|
+
const isActive = activeItem?.href === item.href;
|
|
61
|
+
return (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`));
|
|
62
|
+
}) }));
|
|
45
63
|
}
|
|
46
|
-
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) =>
|
|
64
|
+
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { spacing: "2xs", children: section.items.map((item) => {
|
|
65
|
+
const isActive = activeItem?.href === item.href;
|
|
66
|
+
return (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: isActive ? navItemActiveClass : navItemClass, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`));
|
|
67
|
+
}) }))] }));
|
|
47
68
|
}
|
|
48
69
|
/** Sidebar header when a user is logged in — avatar + display name + close button. */
|
|
49
70
|
function SidebarUserHeader({ user, onClose, }) {
|
|
@@ -61,7 +82,7 @@ function SidebarUserHeader({ user, onClose, }) {
|
|
|
61
82
|
function SidebarGuestHeader({ sidebarTitle, onClose, }) {
|
|
62
83
|
return (_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Div, { textWeight: "semibold", textSize: "sm", color: "primary", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "rounded-full p-[var(--appkit-space-2)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-200 hover:text-zinc-900 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
63
84
|
}
|
|
64
|
-
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
85
|
+
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, activeHref, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
65
86
|
const hasLegacyItems = sidebarItems.length > 0;
|
|
66
87
|
const hasSections = !!(sidebarSections && sidebarSections.length > 0);
|
|
67
88
|
const isAuthenticated = !!user;
|
|
@@ -79,7 +100,20 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
79
100
|
storeDashboard: sidebarProfileLabels?.storeDashboard ?? sidebarProfileLabels?.sellerDashboard ?? "Store Dashboard",
|
|
80
101
|
logout: sidebarProfileLabels?.logout ?? "Logout",
|
|
81
102
|
};
|
|
82
|
-
const navItemClass = "flex w-full items-center justify-end gap-[var(--appkit-space-2)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-300";
|
|
103
|
+
const navItemClass = "flex w-full items-center justify-end gap-[var(--appkit-space-2)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 text-[var(--appkit-color-text-muted)] dark:hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-300";
|
|
104
|
+
const navItemActiveClass = "flex w-full items-center justify-end gap-[var(--appkit-space-2)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] font-medium bg-primary-50 text-primary-800 dark:bg-primary-900/25 dark:text-primary-300";
|
|
105
|
+
const profileLinks = [
|
|
106
|
+
{ href: profileHref },
|
|
107
|
+
...(userOrdersHref ? [{ href: userOrdersHref }] : []),
|
|
108
|
+
...(userWishlistHref ? [{ href: userWishlistHref }] : []),
|
|
109
|
+
...(userSettingsHref ? [{ href: userSettingsHref }] : []),
|
|
110
|
+
];
|
|
111
|
+
const activeProfileLink = findActiveNavItem(profileLinks, activeHref);
|
|
112
|
+
const dashboardLinks = [
|
|
113
|
+
...(adminHref ? [{ href: adminHref }] : []),
|
|
114
|
+
...(resolvedStoreHref ? [{ href: resolvedStoreHref }] : []),
|
|
115
|
+
];
|
|
116
|
+
const activeDashboardLink = findActiveNavItem(dashboardLinks, activeHref);
|
|
83
117
|
const normalizedSections = hasSections
|
|
84
118
|
? sidebarSections
|
|
85
119
|
: hasLegacyItems
|
|
@@ -90,7 +124,7 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
90
124
|
action.variant === "outline"
|
|
91
125
|
? "border border-zinc-300 text-zinc-700 hover:bg-zinc-50 border-[var(--appkit-color-border)] text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-surface-elevated)]"
|
|
92
126
|
: "bg-primary text-white hover:bg-primary-600 dark:bg-primary dark:hover:bg-primary-600 btn-glow",
|
|
93
|
-
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.itemsSold }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: navItemClass, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-50 hover:text-primary-800", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
127
|
+
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.itemsSold }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, hasActive: !!activeDashboardLink, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: activeDashboardLink?.href === adminHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === adminHref ? "page" : undefined, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: activeDashboardLink?.href === resolvedStoreHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === resolvedStoreHref ? "page" : undefined, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, activeHref: activeHref }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-50 hover:text-primary-800", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
94
128
|
}
|
|
95
129
|
// ─── Main component ────────────────────────────────────────────────────────────
|
|
96
130
|
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, onTourStart, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
@@ -139,7 +173,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
139
173
|
const hasBottomActions = bottomActionsState.actions.length > 0 ||
|
|
140
174
|
!!(bottomActionsState.bulk && bottomActionsState.bulk.selectedCount > 0) ||
|
|
141
175
|
!!bottomActionsState.infoLabel;
|
|
142
|
-
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
176
|
+
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
143
177
|
const normalizedLightBackground = {
|
|
144
178
|
type: lightBackground.type,
|
|
145
179
|
value: lightBackground.value,
|
|
@@ -11,7 +11,7 @@ function DefaultNavItem({ item, isActive, }) {
|
|
|
11
11
|
const activeClasses = item.highlighted
|
|
12
12
|
? "border border-primary-400/40 dark:border-secondary-400/30 text-primary-700 dark:text-secondary-400 bg-primary-50/80 dark:bg-secondary-900/30 px-[var(--appkit-space-3)] transition-colors duration-150"
|
|
13
13
|
: isActive
|
|
14
|
-
? "bg-primary-50 bg-[var(--appkit-color-surface-elevated)] text-primary-800 dark:text-white font-semibold px-[var(--appkit-space-3)] border-b-2 border-primary-500 dark:border-secondary-400 rounded-none pb-[6px] transition-colors duration-150"
|
|
14
|
+
? "bg-primary-50 dark:bg-[var(--appkit-color-surface-elevated)] text-primary-800 dark:text-white font-semibold px-[var(--appkit-space-3)] border-b-2 border-primary-500 dark:border-secondary-400 rounded-none pb-[6px] transition-colors duration-150"
|
|
15
15
|
: "text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-zinc-950 dark:hover:text-white transition-colors duration-150 px-[var(--appkit-space-3)]";
|
|
16
16
|
return (_jsxs(Link, { href: item.href, "aria-current": isActive ? "page" : undefined, className: `flex items-center gap-[var(--appkit-space-1-5)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] rounded-lg font-medium transition-colors duration-150 whitespace-nowrap ${activeClasses}`, children: [item.icon, _jsx(Span, { children: item.label })] }));
|
|
17
17
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { Avatar, BlockHeader, Button, Div, Row, Section, SiteLogo, SiteMark, Span } from "../../ui";
|
|
4
4
|
/** Shared icon-button class for all action buttons in the title bar. */
|
|
5
|
-
const iconBtn = "flex items-center justify-center w-9 h-9 rounded-lg text-[var(--appkit-color-text-muted)] hover:bg-primary-50 hover:text-primary-700 hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-400 transition-colors";
|
|
5
|
+
const iconBtn = "flex items-center justify-center w-9 h-9 rounded-lg text-[var(--appkit-color-text-muted)] hover:bg-primary-50 hover:text-primary-700 dark:hover:bg-[var(--appkit-color-surface-elevated)] dark:hover:text-secondary-400 transition-colors";
|
|
6
6
|
/** Badge counter class for wishlist/cart counts. */
|
|
7
7
|
const countBadge = "absolute -top-0.5 -right-0.5 flex items-center justify-center w-4 h-4 rounded-full bg-error-surface text-white text-[10px] font-bold leading-none";
|
|
8
8
|
/**
|
|
@@ -30,7 +30,7 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
30
30
|
const wishlistEl = wishlistHref ? (_jsxs(Link, { href: wishlistHref, "aria-label": `Wishlist${wishlistCount > 0 ? `, ${wishlistCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-wishlist", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { className: countBadge, children: wishlistCount > 9 ? "9+" : wishlistCount }))] })) : null;
|
|
31
31
|
const cartEl = cartHref ? (_jsxs(Link, { href: cartHref, "aria-label": `Cart${cartCount > 0 ? `, ${cartCount} items` : ""}`, className: `relative ${iconBtn}`, "data-tour": "nav-cart", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) }), cartCount > 0 && (_jsx(Span, { className: countBadge, children: cartCount > 9 ? "9+" : cartCount }))] })) : null;
|
|
32
32
|
const profileEl = profileHref ? (_jsx(Link, { href: profileHref, "aria-label": user ? `Profile — ${user.displayName ?? user.email}` : "Sign in", className: `relative ${iconBtn}`, "data-tour": "nav-profile", children: _jsx(Avatar, { src: user?.photoURL ?? undefined, name: user?.displayName ?? user?.email ?? undefined, alt: user?.displayName ?? "Profile", size: "sm" }) })) : null;
|
|
33
|
-
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 hover:bg-[var(--appkit-color-surface-elevated)]", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
33
|
+
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-text-muted)] hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 dark:hover:bg-[var(--appkit-color-surface-elevated)]", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
34
34
|
const hasTb2 = !!(notificationsEl || wishlistEl || cartEl || profileEl);
|
|
35
35
|
// ── Render ───────────────────────────────────────────────────────────────────
|
|
36
36
|
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b border-[var(--appkit-color-border-subtle)] shadow-sm ${className}`, children: [promoStripText && (_jsx(Section, { color: "inverse", tone: "accent-banner", className: "text-[length:var(--appkit-text-xs)] text-center font-medium", padding: "y-2xs", children: promoStripText })), _jsxs(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "relative h-14", children: [_jsx(Row, { gap: "3", children: _jsxs(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center gap-[var(--appkit-space-2)] transition-opacity hover:opacity-80", children: [_jsx(Div, { className: "flex md:hidden", children: _jsx(SiteMark, { title: brandName, size: "sm" }) }), _jsx(SiteLogo, { title: brandName, size: "md" })] }) }), _jsx(Row, { className: "hidden md:flex absolute inset-y-0 left-1/2 -translate-x-1/2", align: "center", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteMark, { src: siteLogoUrl, title: brandName, size: "lg" }) }) }), _jsxs(Row, { gap: "xs", children: [devSlot, compareEl, notificationSlot, notificationsEl && _jsx(Div, { className: "hidden lg:flex", children: notificationsEl }), wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* client-side overlay covers `<video>` playback because there is no
|
|
6
6
|
* Vercel-Hobby-budget ffmpeg pipeline to bake the watermark into the frames.
|
|
7
7
|
*/
|
|
8
|
+
export type MediaVideoWatermarkPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center" | "custom";
|
|
8
9
|
export interface MediaVideoWatermark {
|
|
9
10
|
/** `"text"` renders a `<Span>` overlay; `"image"` renders a `<MediaImage>` (proxied). */
|
|
10
11
|
type: "text" | "image";
|
|
@@ -16,6 +17,12 @@ export interface MediaVideoWatermark {
|
|
|
16
17
|
size?: number;
|
|
17
18
|
/** Percentage opacity (0–100). Default 20. */
|
|
18
19
|
opacity?: number;
|
|
20
|
+
/** Anchor preset — mirrors the server-side Sharp pipeline. Default `"center"`. */
|
|
21
|
+
position?: MediaVideoWatermarkPosition;
|
|
22
|
+
/** `position === "custom"` only: % of container width, from center. +right / -left. */
|
|
23
|
+
offsetX?: number;
|
|
24
|
+
/** `position === "custom"` only: % of container height, from center. +down / -up. */
|
|
25
|
+
offsetY?: number;
|
|
19
26
|
}
|
|
20
27
|
export interface MediaVideoProps {
|
|
21
28
|
/** Video URL. When undefined the fallback placeholder is rendered. */
|
|
@@ -48,6 +48,39 @@ export function MediaVideo({ src, thumbnailUrl, alt = "Video", controls = true,
|
|
|
48
48
|
}
|
|
49
49
|
return (_jsxs(_Fragment, { children: [_jsx("video", { ref: videoRef, src: resolvedSrc, poster: resolvedPoster, controls: controls, autoPlay: autoPlayMuted, muted: autoPlayMuted, loop: loop, playsInline: true, "aria-label": alt, className: `absolute inset-0 w-full h-full ${fitClass}` }), effectiveWatermark ? _jsx(MediaVideoWatermarkLayer, { config: effectiveWatermark }) : null] }));
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Mirrors the server-side Sharp pipeline's 5 anchor presets + `"custom"`
|
|
53
|
+
* %-offset-from-center (`resolveCompositePlacement` in
|
|
54
|
+
* `src/app/api/media/_watermark.ts`) as CSS insets, so a video's live
|
|
55
|
+
* overlay and its poster-frame-as-image watermark land in the same spot.
|
|
56
|
+
* Default `"center"`, 0/0 offset — matches the server-side default too.
|
|
57
|
+
*/
|
|
58
|
+
function resolveWatermarkPlacementStyle(config) {
|
|
59
|
+
const position = config.position ?? "center";
|
|
60
|
+
const inset = "2%";
|
|
61
|
+
switch (position) {
|
|
62
|
+
case "top-left":
|
|
63
|
+
return { top: inset, left: inset };
|
|
64
|
+
case "top-right":
|
|
65
|
+
return { top: inset, right: inset };
|
|
66
|
+
case "bottom-left":
|
|
67
|
+
return { bottom: inset, left: inset };
|
|
68
|
+
case "bottom-right":
|
|
69
|
+
return { bottom: inset, right: inset };
|
|
70
|
+
case "custom": {
|
|
71
|
+
const offsetX = Math.max(-45, Math.min(45, config.offsetX ?? 0));
|
|
72
|
+
const offsetY = Math.max(-45, Math.min(45, config.offsetY ?? 0));
|
|
73
|
+
return {
|
|
74
|
+
top: `calc(50% + ${offsetY}%)`,
|
|
75
|
+
left: `calc(50% + ${offsetX}%)`,
|
|
76
|
+
transform: "translate(-50%, -50%)",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case "center":
|
|
80
|
+
default:
|
|
81
|
+
return { top: "50%", left: "50%", transform: "translate(-50%, -50%)" };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
51
84
|
/**
|
|
52
85
|
* Visual watermark overlay rendered on top of the playing `<video>`. Native
|
|
53
86
|
* video controls render in a higher layer (the browser shadow DOM), so this
|
|
@@ -62,16 +95,14 @@ function MediaVideoWatermarkLayer({ config }) {
|
|
|
62
95
|
const opacity = Math.max(0, Math.min(100, config.opacity ?? 20)) / 100;
|
|
63
96
|
if (widthPct === 0 || opacity === 0)
|
|
64
97
|
return null;
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// with the video, just like the server-side Sharp pipeline scales images.
|
|
98
|
+
// Width is a percentage of the container so the watermark scales with the
|
|
99
|
+
// video, just like the server-side Sharp pipeline scales images.
|
|
68
100
|
const containerStyle = {
|
|
69
101
|
position: "absolute",
|
|
70
|
-
right: "2%",
|
|
71
|
-
bottom: "2%",
|
|
72
102
|
width: `${widthPct}%`,
|
|
73
103
|
pointerEvents: "none",
|
|
74
104
|
opacity,
|
|
105
|
+
...resolveWatermarkPlacementStyle(config),
|
|
75
106
|
};
|
|
76
107
|
if (config.type === "image" && config.imageUrl) {
|
|
77
108
|
return (_jsx(Div, { className: "z-10", style: containerStyle, "aria-hidden": "true", children: _jsx("img", { src: resolveMediaUrl(config.imageUrl), alt: "", className: "block w-full h-auto" }) }));
|
|
@@ -29,13 +29,9 @@ export function OrderCard({ order, onClick, labels = {}, renderActions }) {
|
|
|
29
29
|
it.prizeRevealStatus !== undefined &&
|
|
30
30
|
it.prizeRevealStatus !== "revealed");
|
|
31
31
|
const revealsRemaining = unrevealedPrizeDraws.length;
|
|
32
|
-
const earliestDeadline = unrevealedPrizeDraws
|
|
33
|
-
.map((it) => it.prizeRevealDeadline)
|
|
34
|
-
.filter((d) => !!d)
|
|
35
|
-
.sort()[0];
|
|
36
32
|
return (_jsxs(Div, { padding: "5", role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
|
|
37
33
|
? (e) => (e.key === "Enter" || e.key === " ") && onClick(order)
|
|
38
|
-
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `border border-neutral-200 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, rounded: "xl", surface: "default", children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5", color: "faint", size: "xs", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `capitalize ${statusColor}`, rounded: "full", padding: "pill-md", children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (
|
|
34
|
+
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `border border-neutral-200 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, rounded: "xl", surface: "default", children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5", color: "faint", size: "xs", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `capitalize ${statusColor}`, rounded: "full", padding: "pill-md", children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsx(Row, { gap: "sm", className: "mt-2", children: _jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }) })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { gap: "sm", children: [item.image && (_jsx(Div, { className: "relative h-10 w-10 flex-shrink-0", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) })), _jsxs(Div, { children: [_jsx(Text, { truncate: true, size: "sm", weight: "medium", color: "primary", children: item.title }), _jsxs(Text, { size: "xs", color: "faint", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { size: "xs", className: "self-center", color: "faint", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100", padding: "t-sm", children: [_jsxs(Span, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: [order.currency ?? "", " Total"] }), _jsx(Span, { weight: "semibold", className: "text-[var(--appkit-color-text)]", children: formatCurrency(order.total, order.currency) })] }), renderActions && (_jsx(Row, { justify: "end", gap: "sm", className: "mt-3", onClick: (e) => e.stopPropagation(), children: renderActions(order) }))] }));
|
|
39
35
|
}
|
|
40
36
|
export function OrdersList({ orders, isLoading, onOrderClick, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No orders found", renderActions, }) {
|
|
41
37
|
if (isLoading) {
|
|
@@ -92,10 +92,6 @@ export interface OrderDocumentItem {
|
|
|
92
92
|
listingType?: ListingType;
|
|
93
93
|
/** SB8-F â€" per-item reveal status; flips through pending â†' open â†' revealed/closed. */
|
|
94
94
|
prizeRevealStatus?: "pending" | "open" | "closed" | "revealed";
|
|
95
|
-
/** SB8-F â€" ISO timestamp; deadline by which the buyer must claim the prize. */
|
|
96
|
-
prizeRevealDeadline?: string;
|
|
97
|
-
/** SB8-F â€" set after the reveal API picks a winner. */
|
|
98
|
-
revealedItemNumber?: number;
|
|
99
95
|
/** Set when the buyer cancels this specific line item post-order (partial cancellation). */
|
|
100
96
|
cancelledQuantity?: number;
|
|
101
97
|
cancelledAt?: Date;
|
|
@@ -236,12 +232,10 @@ export interface OrderDocument extends BaseDocument {
|
|
|
236
232
|
images: string[];
|
|
237
233
|
wonAt: Date;
|
|
238
234
|
};
|
|
239
|
-
/** Deadline for the buyer to claim the won prize (typically 7 days). */
|
|
240
|
-
prizeRevealDeadline?: Date;
|
|
241
|
-
/** True once `prizeRevealDeadline` passes without a claim â€" auto-forfeit. */
|
|
242
|
-
prizeRevealExpired?: boolean;
|
|
243
235
|
/** Source product id when the order came from a prize-draw entry. */
|
|
244
236
|
prizeDrawProductId?: string;
|
|
237
|
+
/** Denormalized from the product at order-creation time — drives reveal copy on the order-detail page without an extra product fetch. */
|
|
238
|
+
prizeRevealMode?: "instant" | "scheduled";
|
|
245
239
|
/** True for prize-draw entries and bundle purchases that bypass refund. */
|
|
246
240
|
isNonRefundable?: boolean;
|
|
247
241
|
/** Set when the order came from a bundle â€" points back to `bundles/{id}`. */
|
|
@@ -90,8 +90,6 @@ export declare const orderDocumentItemSchema: z.ZodObject<{
|
|
|
90
90
|
totalPrice: z.ZodEffects<z.ZodNumber, number, number>;
|
|
91
91
|
listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
|
|
92
92
|
prizeRevealStatus: z.ZodOptional<z.ZodEnum<["pending", "open", "closed", "revealed"]>>;
|
|
93
|
-
prizeRevealDeadline: z.ZodOptional<z.ZodString>;
|
|
94
|
-
revealedItemNumber: z.ZodOptional<z.ZodNumber>;
|
|
95
93
|
}, "strip", z.ZodTypeAny, {
|
|
96
94
|
productId: string;
|
|
97
95
|
quantity: number;
|
|
@@ -100,8 +98,6 @@ export declare const orderDocumentItemSchema: z.ZodObject<{
|
|
|
100
98
|
totalPrice: number;
|
|
101
99
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
102
100
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
103
|
-
prizeRevealDeadline?: string | undefined;
|
|
104
|
-
revealedItemNumber?: number | undefined;
|
|
105
101
|
}, {
|
|
106
102
|
productId: string;
|
|
107
103
|
quantity: number;
|
|
@@ -110,8 +106,6 @@ export declare const orderDocumentItemSchema: z.ZodObject<{
|
|
|
110
106
|
totalPrice: number;
|
|
111
107
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
112
108
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
113
|
-
prizeRevealDeadline?: string | undefined;
|
|
114
|
-
revealedItemNumber?: number | undefined;
|
|
115
109
|
}>;
|
|
116
110
|
export declare const orderRefundEventSchema: z.ZodObject<{
|
|
117
111
|
refundId: z.ZodString;
|
|
@@ -255,8 +249,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
255
249
|
totalPrice: z.ZodEffects<z.ZodNumber, number, number>;
|
|
256
250
|
listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "classified", "digital-code", "live"]>>;
|
|
257
251
|
prizeRevealStatus: z.ZodOptional<z.ZodEnum<["pending", "open", "closed", "revealed"]>>;
|
|
258
|
-
prizeRevealDeadline: z.ZodOptional<z.ZodString>;
|
|
259
|
-
revealedItemNumber: z.ZodOptional<z.ZodNumber>;
|
|
260
252
|
}, "strip", z.ZodTypeAny, {
|
|
261
253
|
productId: string;
|
|
262
254
|
quantity: number;
|
|
@@ -265,8 +257,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
265
257
|
totalPrice: number;
|
|
266
258
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
267
259
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
268
|
-
prizeRevealDeadline?: string | undefined;
|
|
269
|
-
revealedItemNumber?: number | undefined;
|
|
270
260
|
}, {
|
|
271
261
|
productId: string;
|
|
272
262
|
quantity: number;
|
|
@@ -275,8 +265,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
275
265
|
totalPrice: number;
|
|
276
266
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
277
267
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
278
|
-
prizeRevealDeadline?: string | undefined;
|
|
279
|
-
revealedItemNumber?: number | undefined;
|
|
280
268
|
}>, "many">>;
|
|
281
269
|
orderType: z.ZodOptional<z.ZodString>;
|
|
282
270
|
imageUrls: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -450,15 +438,8 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
450
438
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
451
439
|
}, z.ZodTypeAny, "passthrough">;
|
|
452
440
|
}>>;
|
|
453
|
-
prizeRevealDeadline: z.ZodOptional<z.ZodUnion<[z.ZodDate, z.ZodString, z.ZodObject<{
|
|
454
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
455
|
-
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
456
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
457
|
-
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
458
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
459
|
-
}, z.ZodTypeAny, "passthrough">>]>>;
|
|
460
|
-
prizeRevealExpired: z.ZodOptional<z.ZodBoolean>;
|
|
461
441
|
prizeDrawProductId: z.ZodOptional<z.ZodString>;
|
|
442
|
+
prizeRevealMode: z.ZodOptional<z.ZodEnum<["instant", "scheduled"]>>;
|
|
462
443
|
isNonRefundable: z.ZodOptional<z.ZodBoolean>;
|
|
463
444
|
bundleId: z.ZodOptional<z.ZodString>;
|
|
464
445
|
paymentBatchId: z.ZodOptional<z.ZodString>;
|
|
@@ -582,6 +563,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
582
563
|
paymentMethod?: string | undefined;
|
|
583
564
|
notes?: string | undefined;
|
|
584
565
|
storeName?: string | undefined;
|
|
566
|
+
prizeRevealMode?: "instant" | "scheduled" | undefined;
|
|
585
567
|
physicalLocation?: {
|
|
586
568
|
zone: string;
|
|
587
569
|
shelf: string;
|
|
@@ -595,8 +577,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
595
577
|
totalPrice: number;
|
|
596
578
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
597
579
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
598
|
-
prizeRevealDeadline?: string | undefined;
|
|
599
|
-
revealedItemNumber?: number | undefined;
|
|
600
580
|
}[] | undefined;
|
|
601
581
|
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
602
582
|
couponCode?: string | undefined;
|
|
@@ -668,10 +648,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
668
648
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
669
649
|
}, z.ZodTypeAny, "passthrough">;
|
|
670
650
|
} | undefined;
|
|
671
|
-
prizeRevealDeadline?: string | Date | z.objectOutputType<{
|
|
672
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
673
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
674
|
-
prizeRevealExpired?: boolean | undefined;
|
|
675
651
|
prizeDrawProductId?: string | undefined;
|
|
676
652
|
isNonRefundable?: boolean | undefined;
|
|
677
653
|
bundleId?: string | undefined;
|
|
@@ -735,6 +711,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
735
711
|
paymentMethod?: string | undefined;
|
|
736
712
|
notes?: string | undefined;
|
|
737
713
|
storeName?: string | undefined;
|
|
714
|
+
prizeRevealMode?: "instant" | "scheduled" | undefined;
|
|
738
715
|
physicalLocation?: {
|
|
739
716
|
zone: string;
|
|
740
717
|
shelf: string;
|
|
@@ -748,8 +725,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
748
725
|
totalPrice: number;
|
|
749
726
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
750
727
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
751
|
-
prizeRevealDeadline?: string | undefined;
|
|
752
|
-
revealedItemNumber?: number | undefined;
|
|
753
728
|
}[] | undefined;
|
|
754
729
|
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
755
730
|
couponCode?: string | undefined;
|
|
@@ -821,10 +796,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
821
796
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
822
797
|
}, z.ZodTypeAny, "passthrough">;
|
|
823
798
|
} | undefined;
|
|
824
|
-
prizeRevealDeadline?: string | Date | z.objectInputType<{
|
|
825
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
826
|
-
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
827
|
-
prizeRevealExpired?: boolean | undefined;
|
|
828
799
|
prizeDrawProductId?: string | undefined;
|
|
829
800
|
isNonRefundable?: boolean | undefined;
|
|
830
801
|
bundleId?: string | undefined;
|
|
@@ -51,8 +51,6 @@ export const orderDocumentItemSchema = z.object({
|
|
|
51
51
|
totalPrice: rupeesSchema,
|
|
52
52
|
listingType: orderListingTypeSchema.optional(),
|
|
53
53
|
prizeRevealStatus: z.enum(["pending", "open", "closed", "revealed"]).optional(),
|
|
54
|
-
prizeRevealDeadline: z.string().optional(),
|
|
55
|
-
revealedItemNumber: z.number().int().optional(),
|
|
56
54
|
});
|
|
57
55
|
export const orderRefundEventSchema = z.object({
|
|
58
56
|
refundId: z.string(),
|
|
@@ -152,9 +150,8 @@ export const orderFirestoreSchema = z.object({
|
|
|
152
150
|
payoutId: z.string().optional(),
|
|
153
151
|
offerId: z.string().optional(),
|
|
154
152
|
prizeWon: prizeWonSchema.optional(),
|
|
155
|
-
prizeRevealDeadline: firestoreDateSchema.optional(),
|
|
156
|
-
prizeRevealExpired: z.boolean().optional(),
|
|
157
153
|
prizeDrawProductId: z.string().optional(),
|
|
154
|
+
prizeRevealMode: z.enum(["instant", "scheduled"]).optional(),
|
|
158
155
|
isNonRefundable: z.boolean().optional(),
|
|
159
156
|
bundleId: z.string().optional(),
|
|
160
157
|
paymentBatchId: z.string().optional(),
|
|
@@ -32,10 +32,6 @@ export interface OrderItem {
|
|
|
32
32
|
* "X reveals pending" badge on user orders.
|
|
33
33
|
*/
|
|
34
34
|
prizeRevealStatus?: "pending" | "open" | "closed" | "revealed";
|
|
35
|
-
/** ISO timestamp — when the user must claim their reveal before forfeit. */
|
|
36
|
-
prizeRevealDeadline?: string;
|
|
37
|
-
/** Set after the reveal endpoint picks a winner — the prize item index. */
|
|
38
|
-
revealedItemNumber?: number;
|
|
39
35
|
/** Set when the buyer cancels this specific line item post-order (partial cancellation). */
|
|
40
36
|
cancelledQuantity?: number;
|
|
41
37
|
/**
|
|
@@ -85,9 +81,10 @@ export interface Order {
|
|
|
85
81
|
disputeRaised?: boolean;
|
|
86
82
|
disputeStatus?: "open" | "resolved";
|
|
87
83
|
/**
|
|
88
|
-
* Reveal-mode prize-draw fields (SB4-H/SB8-C). Set once the
|
|
89
|
-
*
|
|
90
|
-
*
|
|
84
|
+
* Reveal-mode prize-draw fields (SB4-H/SB8-C). Set once the winner has
|
|
85
|
+
* been automatically assigned to this order (on payment confirmation,
|
|
86
|
+
* draw expiry, or sellout — see assignPrizeDrawWinner); drives the
|
|
87
|
+
* PrizeRevealModal's "won" display on the order-detail page.
|
|
91
88
|
*/
|
|
92
89
|
prizeWon?: {
|
|
93
90
|
itemNumber: number;
|
|
@@ -95,10 +92,10 @@ export interface Order {
|
|
|
95
92
|
images: string[];
|
|
96
93
|
wonAt: string;
|
|
97
94
|
};
|
|
98
|
-
prizeRevealDeadline?: string;
|
|
99
|
-
prizeRevealExpired?: boolean;
|
|
100
95
|
/** Source prize-draw product id, when this order came from one. */
|
|
101
96
|
prizeDrawProductId?: string;
|
|
97
|
+
/** Denormalized from the product at order-creation time — drives PrizeRevealModal copy. */
|
|
98
|
+
prizeRevealMode?: "instant" | "scheduled";
|
|
102
99
|
}
|
|
103
100
|
export interface OrderListResponse {
|
|
104
101
|
items: Order[];
|
|
@@ -64,5 +64,5 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
|
|
|
64
64
|
/* ── Compact list layout ── */
|
|
65
65
|
_jsxs(_Fragment, { children: [_jsxs(Row, { className: "min-w-0", align: "start", justify: "between", gap: "sm", children: [_jsx(TextLink, { href: detailHref, className: "min-w-0 flex-1", children: _jsx(Text, { size: "sm", weight: "medium", color: "primary", truncate: 2, children: product.title }) }), wishlistActions && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `shrink-0 p-[0.25rem] leading-none ${inWishlist ? "text-primary" : "text-[var(--appkit-color-text-faint)]"}`, onClick: handleWishlist, "aria-label": inWishlist ? mergedLabels.removeFromWishlist : mergedLabels.addToWishlist, children: inWishlist ? "♥" : "♡" }))] }), _jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Text, { className: "text-primary", size: "sm", weight: "bold", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsx(Button, { type: "button", variant: "primary", size: "sm", textSize: "xs", className: "self-start mt-0.5", onClick: handleNavigate, children: mergedLabels.reserveNow })] })) : (
|
|
66
66
|
/* ── Full grid layout ── */
|
|
67
|
-
_jsxs(_Fragment, { children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { size: "sm", weight: "medium", color: "primary", truncate: 2, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "line-clamp-2 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]" })) : null, _jsxs(Row, { justify: "between", className: "mt-1", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-2", gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", textSize: "xs", className: "flex-1", onClick: handleNavigate, children: mergedLabels.reserveNow }), onAddToCart ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", textSize: "xs", className: "flex-1", onClick: (e) => { e.stopPropagation(); onAddToCart(product); }, children: mergedLabels.addToCart })) : null, wishlistActions ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `${inWishlist ? "text-primary" : "text-[var(--appkit-color-text-muted)]"}`, onClick: handleWishlist, "aria-label": inWishlist ? mergedLabels.removeFromWishlist : mergedLabels.addToWishlist, children: inWishlist ? "♥" : "♡" })) : null] })] })) })] }));
|
|
67
|
+
_jsxs(_Fragment, { children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { size: "sm", weight: "medium", color: "primary", truncate: 2, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "line-clamp-2 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]" })) : null, _jsxs(Row, { justify: "between", className: "mt-1", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-auto pt-[var(--appkit-space-2)]", gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", textSize: "xs", className: "flex-1", onClick: handleNavigate, children: mergedLabels.reserveNow }), onAddToCart ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", textSize: "xs", className: "flex-1", onClick: (e) => { e.stopPropagation(); onAddToCart(product); }, children: mergedLabels.addToCart })) : null, wishlistActions ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `${inWishlist ? "text-primary" : "text-[var(--appkit-color-text-muted)]"}`, onClick: handleWishlist, "aria-label": inWishlist ? mergedLabels.removeFromWishlist : mergedLabels.addToWishlist, children: inWishlist ? "♥" : "♡" })) : null] })] })) })] }));
|
|
68
68
|
}
|
|
@@ -82,6 +82,7 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
82
82
|
: typeof p.mainImage === "string"
|
|
83
83
|
? [p.mainImage]
|
|
84
84
|
: [];
|
|
85
|
+
const productVideo = p.video;
|
|
85
86
|
const reservedCount = typeof p.preOrderCurrentCount === "number"
|
|
86
87
|
? p.preOrderCurrentCount
|
|
87
88
|
: typeof p.reservedCount === "number"
|
|
@@ -150,7 +151,7 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
150
151
|
price: typeof p.price === "number" ? p.price : undefined,
|
|
151
152
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
152
153
|
storeName: typeof p.storeName === "string" ? p.storeName : undefined,
|
|
153
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: CLS_BREADCRUMB_LINK, children: "Pre-Orders" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => (_jsx(PreOrderInfoSection, { title: title, productionStatus: productionStatus, maxPerUser: maxPerUser, deliveryDate: deliveryDate, featured: featured, freeShipping: freeShipping, condition: condition, isCancellable: isCancellable, category: category, categoryName: categoryName, brand: brand, brandSlug: brandSlug, productFeatures: productFeatures, features: features, descriptionHtml: descriptionHtml, safeSeller: safeSeller, storeHref: storeHref })), renderSublistingSection: sublistingCategoryId
|
|
154
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: CLS_BREADCRUMB_LINK, children: "Pre-Orders" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => (_jsx(PreOrderInfoSection, { title: title, productionStatus: productionStatus, maxPerUser: maxPerUser, deliveryDate: deliveryDate, featured: featured, freeShipping: freeShipping, condition: condition, isCancellable: isCancellable, category: category, categoryName: categoryName, brand: brand, brandSlug: brandSlug, productFeatures: productFeatures, features: features, descriptionHtml: descriptionHtml, safeSeller: safeSeller, storeHref: storeHref })), renderSublistingSection: sublistingCategoryId
|
|
154
155
|
? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
|
|
155
156
|
: undefined, renderGroupSection: groupId
|
|
156
157
|
? () => (_jsx(ShowGroupSection, { groupId: groupId, currentSlug: String(p.slug ?? product.id), isParent: isGroupParent, groupTitle: groupTitle }))
|
|
@@ -101,16 +101,19 @@ export async function listPreOrders(params = {}) {
|
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
103
|
export async function getRelatedProducts(categoryId, excludeId, limit = 6) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
// `categoryId` here is really a category slug — products store their
|
|
105
|
+
// category FK in categorySlugs[] (array-contains), not a scalar
|
|
106
|
+
// "categoryId" field, which never existed on ProductDocument.
|
|
107
|
+
const docs = await productRepository.findByCategory(categoryId);
|
|
108
|
+
const items = docs
|
|
109
|
+
.filter((p) => p.id !== excludeId && p.status === ProductStatusValues.PUBLISHED)
|
|
110
|
+
.sort((a, b) => {
|
|
111
|
+
const aTime = a.createdAt instanceof Date ? a.createdAt.getTime() : 0;
|
|
112
|
+
const bTime = b.createdAt instanceof Date ? b.createdAt.getTime() : 0;
|
|
113
|
+
return bTime - aTime;
|
|
114
|
+
})
|
|
115
|
+
.slice(0, limit);
|
|
116
|
+
return { items, total: items.length, page: 1, pageSize: limit, totalPages: 1, hasMore: false };
|
|
114
117
|
}
|
|
115
118
|
export async function getStoreStorefrontProducts(storeId) {
|
|
116
119
|
const products = await productRepository.findByStore(storeId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CategoryDocument } from "../../categories/schemas/firestore";
|
|
2
|
-
export type MarketplaceBundleCardData = Pick<CategoryDocument, "id" | "name" | "slug" | "bundleItemDetails" | "bundleProductIds" | "bundlePrice" | "bundleStockStatus" | "display">;
|
|
2
|
+
export type MarketplaceBundleCardData = Pick<CategoryDocument, "id" | "name" | "slug" | "bundleItemDetails" | "bundleProductIds" | "bundlePrice" | "bundleOriginalTotal" | "bundleStockStatus" | "display">;
|
|
3
3
|
export interface MarketplaceBundleCardLabels {
|
|
4
4
|
bundleBadge?: string;
|
|
5
5
|
inStockBadge?: string;
|