@mohasinac/appkit 4.3.2 → 4.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/maintenance/views/AnalysisRunnerView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/ServerErrorDetailView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/ServerErrorsListView.js +1 -1
- package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +14 -3
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +95 -56
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +8 -2
- package/dist/_internal/shared/actions/action-registry.js +15 -0
- package/dist/_internal/shared/features/brands/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +4 -4
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/client.d.ts +36 -0
- package/dist/client.js +30 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/constants/field-names.d.ts +7 -0
- package/dist/constants/field-names.js +8 -0
- package/dist/features/about/components/DeveloperView.d.ts +16 -0
- package/dist/features/about/components/DeveloperView.js +29 -0
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +1 -0
- package/dist/features/account/schemas/index.d.ts +16 -16
- package/dist/features/addresses/schemas/index.d.ts +8 -8
- package/dist/features/admin/components/AdminAddressesView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +2 -2
- package/dist/features/admin/components/AdminArtView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +2 -2
- package/dist/features/admin/components/AdminBlogView.js +2 -2
- package/dist/features/admin/components/AdminBrandsView.js +2 -2
- package/dist/features/admin/components/AdminBundlesView.js +2 -2
- package/dist/features/admin/components/AdminCartsView.js +2 -2
- package/dist/features/admin/components/AdminCategoriesView.js +2 -2
- package/dist/features/admin/components/AdminClassifiedView.js +1 -1
- package/dist/features/admin/components/AdminContactView.js +2 -2
- package/dist/features/admin/components/AdminCouponsView.js +2 -2
- package/dist/features/admin/components/AdminDigitalCodesView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +2 -2
- package/dist/features/admin/components/AdminFeaturesView.js +1 -1
- package/dist/features/admin/components/AdminGroupedListingsView.js +1 -1
- package/dist/features/admin/components/AdminHistoryView.js +1 -1
- package/dist/features/admin/components/AdminLiveView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +2 -2
- package/dist/features/admin/components/AdminNotificationsView.js +2 -2
- package/dist/features/admin/components/AdminOrdersView.js +2 -2
- package/dist/features/admin/components/AdminPaymentMethodsView.js +1 -1
- package/dist/features/admin/components/AdminPayoutsView.js +2 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +2 -2
- package/dist/features/admin/components/AdminProductsView.js +2 -2
- package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
- package/dist/features/admin/components/AdminReviewsView.js +2 -2
- package/dist/features/admin/components/AdminScammersView.js +2 -2
- package/dist/features/admin/components/AdminSessionsView.js +2 -2
- package/dist/features/admin/components/AdminStickersView.js +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
- package/dist/features/admin/components/AdminStoresView.js +2 -2
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
- package/dist/features/admin/components/AdminTeamView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
- package/dist/features/admin/components/AdminUsersView.js +2 -2
- package/dist/features/admin/components/AdminWishlistsView.js +1 -1
- package/dist/features/admin/hooks/useAdminListing.d.ts +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +1 -1
- package/dist/features/blog/actions/blog-actions.d.ts +14 -14
- package/dist/features/blog/schemas/index.d.ts +14 -14
- package/dist/features/cart/hooks/useAddToCart.d.ts +6 -2
- package/dist/features/cart/hooks/useAddToCart.js +20 -3
- package/dist/features/cart/schemas/index.d.ts +3 -3
- package/dist/features/cart/utils/pending-ops.d.ts +20 -0
- package/dist/features/cart/utils/pending-ops.js +22 -0
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/categories/components/CategoryBundlesListing.js +1 -1
- package/dist/features/categories/components/CategoryFilters.js +1 -1
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/schemas/index.d.ts +16 -16
- package/dist/features/events/actions/event-actions.d.ts +21 -1
- package/dist/features/events/actions/event-actions.js +85 -3
- package/dist/features/events/components/AdminEventEditorView.js +11 -2
- package/dist/features/events/components/AdminEventEntriesView.js +37 -4
- package/dist/features/events/components/AdminEventsView.js +2 -2
- package/dist/features/events/components/EventLeaderboard.d.ts +12 -2
- package/dist/features/events/components/EventLeaderboard.js +4 -1
- package/dist/features/events/components/EventRafflesSection.js +2 -2
- package/dist/features/events/repository/event-entry.repository.d.ts +39 -0
- package/dist/features/events/repository/event-entry.repository.js +153 -0
- package/dist/features/events/schemas/firestore.d.ts +28 -1
- package/dist/features/events/schemas/firestore.js +9 -0
- package/dist/features/events/schemas/index.d.ts +13 -13
- package/dist/features/events/types/index.d.ts +26 -0
- package/dist/features/faq/actions/faq-actions.d.ts +8 -8
- package/dist/features/faq/schemas/index.d.ts +22 -22
- package/dist/features/grouped/schemas/index.d.ts +2 -2
- package/dist/features/homepage/components/AnnouncementBar.d.ts +9 -1
- package/dist/features/homepage/components/AnnouncementBar.js +6 -3
- package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
- package/dist/features/homepage/components/SectionCarousel.js +5 -1
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/homepage/schemas/index.d.ts +2 -2
- package/dist/features/layout/BottomNavbar.js +8 -6
- package/dist/features/lottery/schemas/zod.d.ts +10 -10
- package/dist/features/media/MediaVideo.js +11 -3
- package/dist/features/orders/schemas/index.d.ts +52 -52
- package/dist/features/products/components/CompareOverlay.d.ts +9 -0
- package/dist/features/products/components/CompareOverlay.js +28 -3
- package/dist/features/products/components/InteractiveProductCard.js +9 -3
- package/dist/features/products/components/MakeOfferForm.js +1 -1
- package/dist/features/products/components/PlaceBidForm.js +1 -1
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawsSection.js +2 -2
- package/dist/features/products/components/ProductDetailActions.js +3 -1
- package/dist/features/products/components/ProductGrid.js +1 -1
- package/dist/features/products/components/ProductInfo.js +1 -1
- package/dist/features/products/components/ProductsIndexListing.js +12 -2
- package/dist/features/products/hooks/useRelatedProducts.js +1 -1
- package/dist/features/products/schemas/index.d.ts +10 -10
- package/dist/features/promotions/schemas/index.d.ts +8 -8
- package/dist/features/reviews/schemas/index.d.ts +12 -12
- package/dist/features/scams/schemas/index.d.ts +6 -6
- package/dist/features/seller/components/CouponInlineSelect.js +1 -1
- package/dist/features/seller/components/SellerArtView.js +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +2 -2
- package/dist/features/seller/components/SellerBidsView.js +2 -2
- package/dist/features/seller/components/SellerBundlesView.js +1 -1
- package/dist/features/seller/components/SellerClassifiedView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +2 -2
- package/dist/features/seller/components/SellerDigitalCodesView.js +2 -2
- package/dist/features/seller/components/SellerGroupedListingsView.js +1 -1
- package/dist/features/seller/components/SellerLiveView.js +2 -2
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerPayoutMethodsView.js +2 -2
- package/dist/features/seller/components/SellerPayoutsView.js +2 -2
- package/dist/features/seller/components/SellerPreOrdersView.js +2 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +2 -2
- package/dist/features/seller/components/SellerShippingConfigsView.js +2 -2
- package/dist/features/seller/components/SellerStickersView.js +2 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
- package/dist/features/seller/components/SublistingInlineSelect.js +1 -1
- package/dist/features/seller/hooks/useSellerListingData.d.ts +1 -1
- package/dist/features/seller/schemas/index.d.ts +16 -16
- package/dist/features/server-errors/schemas/index.d.ts +4 -4
- package/dist/features/shell/StepForm.d.ts +1 -1
- package/dist/features/shipments/components/AdminShipmentsView.js +2 -2
- package/dist/features/stores/components/StoreProductsView.js +1 -1
- package/dist/features/stores/components/StoresIndexListing.js +1 -1
- package/dist/features/stores/schemas/index.d.ts +32 -32
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +68 -1
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +14 -0
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +17 -0
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +2 -8
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +4 -11
- package/dist/features/wishlist/repository/user-wishlist.repository.d.ts +11 -0
- package/dist/features/wishlist/repository/user-wishlist.repository.js +33 -0
- package/dist/index.d.ts +7 -12
- package/dist/index.js +16 -28
- package/dist/next/components/ErrorView.js +1 -1
- package/dist/next/components/NotFoundView.js +1 -1
- package/dist/next/components/UnauthorizedView.js +1 -1
- package/dist/next/routing/route-map.d.ts +4 -0
- package/dist/next/routing/route-map.js +2 -0
- package/dist/react/contexts/SessionContext.d.ts +1 -1
- package/dist/react/hooks/use-action-dispatch.d.ts +1 -1
- package/dist/react/hooks/useFormStatePreservation.d.ts +1 -1
- package/dist/schemas/registry.d.ts +238 -238
- package/dist/schemas/webhooks/razorpay.d.ts +100 -100
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/rate-limit.d.ts +10 -0
- package/dist/security/rate-limit.js +13 -1
- package/dist/seed/events-seed-data.js +52 -1
- package/dist/server.d.ts +6 -0
- package/dist/server.js +8 -0
- package/dist/styles.css +25 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/HorizontalScroller.d.ts +7 -1
- package/dist/ui/components/HorizontalScroller.js +2 -2
- package/dist/ui/components/HorizontalScroller.style.css +24 -0
- package/dist/validation/schemas.d.ts +12 -12
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { AvatarDisplay, Li, Span, TextLink } from "../../ui";
|
|
|
5
5
|
import { BottomNavLayout } from "./BottomNavLayout";
|
|
6
6
|
import { NavItem } from "./NavItem";
|
|
7
7
|
import { useWishlistCount } from "../wishlist/hooks/useWishlistCount";
|
|
8
|
+
import { useCartCount } from "../cart/hooks/useCartCount";
|
|
8
9
|
/**
|
|
9
10
|
* BottomNavbar � mobile bottom navigation bar (5 slots: home, shop, search, cart, profile).
|
|
10
11
|
*
|
|
@@ -13,6 +14,7 @@ import { useWishlistCount } from "../wishlist/hooks/useWishlistCount";
|
|
|
13
14
|
export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishlistHref, profileHref, loginHref, onSearchToggle, navItems, onMoreToggle, activeClassName = "text-primary-600 dark:text-primary-400", inactiveClassName = "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]", iconClassName, labelClassName = "text-[11px] leading-tight font-medium", getRoleBadgeClass, }) {
|
|
14
15
|
const pathname = usePathname();
|
|
15
16
|
const wishlistCount = useWishlistCount(userId);
|
|
17
|
+
const cartCount = useCartCount(!!userId);
|
|
16
18
|
const labels = {
|
|
17
19
|
mobileNav: "Mobile navigation",
|
|
18
20
|
home: "Home",
|
|
@@ -26,12 +28,12 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
26
28
|
const slotClassName = "relative flex h-full w-full flex-col items-center justify-center gap-[var(--appkit-space-1)] text-center transition-colors duration-200";
|
|
27
29
|
const defaultIcon = (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }));
|
|
28
30
|
if (navItems && navItems.length > 0) {
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
const visibleItems = navItems.slice(0,
|
|
34
|
-
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))),
|
|
31
|
+
// Trade one nav-item slot for a dedicated Cart slot so mobile has
|
|
32
|
+
// 1-click cart access with a live item-count badge (previously this
|
|
33
|
+
// slot was Wishlist — Wishlist remains reachable from the header/app
|
|
34
|
+
// bar, it's just no longer duplicated in the bottom tab bar).
|
|
35
|
+
const visibleItems = navItems.slice(0, cartHref ? 3 : 4);
|
|
36
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))), cartHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) })), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }));
|
|
35
37
|
}
|
|
36
38
|
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1 w-1/5", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", 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, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
37
39
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]"}`, weight: "semibold", children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
@@ -42,12 +42,12 @@ export declare const reopenLotterySlotSchema: z.ZodObject<{
|
|
|
42
42
|
adminUserId: z.ZodString;
|
|
43
43
|
}, "strip", z.ZodTypeAny, {
|
|
44
44
|
slotNumber: number;
|
|
45
|
-
sourceType: "
|
|
45
|
+
sourceType: "event" | "product";
|
|
46
46
|
sourceId: string;
|
|
47
47
|
adminUserId: string;
|
|
48
48
|
}, {
|
|
49
49
|
slotNumber: number;
|
|
50
|
-
sourceType: "
|
|
50
|
+
sourceType: "event" | "product";
|
|
51
51
|
sourceId: string;
|
|
52
52
|
adminUserId: string;
|
|
53
53
|
}>;
|
|
@@ -69,45 +69,45 @@ export declare const submitLotteryPullSchema: z.ZodEffects<z.ZodObject<{
|
|
|
69
69
|
transactionId: string;
|
|
70
70
|
userId: string;
|
|
71
71
|
userPhone: string;
|
|
72
|
-
sourceType: "
|
|
72
|
+
sourceType: "event" | "product";
|
|
73
73
|
paymentTime: string;
|
|
74
74
|
purchasedItemNumber: number;
|
|
75
75
|
productId?: string | undefined;
|
|
76
76
|
userEmail?: string | undefined;
|
|
77
|
-
userDisplayName?: string | undefined;
|
|
78
77
|
eventId?: string | undefined;
|
|
78
|
+
userDisplayName?: string | undefined;
|
|
79
79
|
}, {
|
|
80
80
|
transactionId: string;
|
|
81
81
|
userId: string;
|
|
82
82
|
userPhone: string;
|
|
83
|
-
sourceType: "
|
|
83
|
+
sourceType: "event" | "product";
|
|
84
84
|
paymentTime: string;
|
|
85
85
|
purchasedItemNumber: number;
|
|
86
86
|
productId?: string | undefined;
|
|
87
87
|
userEmail?: string | undefined;
|
|
88
|
-
userDisplayName?: string | undefined;
|
|
89
88
|
eventId?: string | undefined;
|
|
89
|
+
userDisplayName?: string | undefined;
|
|
90
90
|
}>, {
|
|
91
91
|
transactionId: string;
|
|
92
92
|
userId: string;
|
|
93
93
|
userPhone: string;
|
|
94
|
-
sourceType: "
|
|
94
|
+
sourceType: "event" | "product";
|
|
95
95
|
paymentTime: string;
|
|
96
96
|
purchasedItemNumber: number;
|
|
97
97
|
productId?: string | undefined;
|
|
98
98
|
userEmail?: string | undefined;
|
|
99
|
-
userDisplayName?: string | undefined;
|
|
100
99
|
eventId?: string | undefined;
|
|
100
|
+
userDisplayName?: string | undefined;
|
|
101
101
|
}, {
|
|
102
102
|
transactionId: string;
|
|
103
103
|
userId: string;
|
|
104
104
|
userPhone: string;
|
|
105
|
-
sourceType: "
|
|
105
|
+
sourceType: "event" | "product";
|
|
106
106
|
paymentTime: string;
|
|
107
107
|
purchasedItemNumber: number;
|
|
108
108
|
productId?: string | undefined;
|
|
109
109
|
userEmail?: string | undefined;
|
|
110
|
-
userDisplayName?: string | undefined;
|
|
111
110
|
eventId?: string | undefined;
|
|
111
|
+
userDisplayName?: string | undefined;
|
|
112
112
|
}>;
|
|
113
113
|
export type SubmitLotteryPullInput = z.infer<typeof submitLotteryPullSchema>;
|
|
@@ -91,15 +91,23 @@ function resolveWatermarkPlacementStyle(config) {
|
|
|
91
91
|
* play/pause toggling.
|
|
92
92
|
*/
|
|
93
93
|
function MediaVideoWatermarkLayer({ config }) {
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// Fallbacks match the server-side Sharp pipeline's DEFAULT_SIZE/DEFAULT_OPACITY
|
|
95
|
+
// (resolve-effective-watermark.ts) — these only kick in if no config is passed
|
|
96
|
+
// at all; in practice callers read siteSettings.effectiveWatermark, which
|
|
97
|
+
// always carries the real size/opacity.
|
|
98
|
+
const widthPct = Math.max(0, Math.min(100, config.size ?? 10));
|
|
99
|
+
const opacity = Math.max(0, Math.min(100, config.opacity ?? 10)) / 100;
|
|
96
100
|
if (widthPct === 0 || opacity === 0)
|
|
97
101
|
return null;
|
|
98
102
|
// Width is a percentage of the container so the watermark scales with the
|
|
99
|
-
// video, just like the server-side Sharp pipeline scales images
|
|
103
|
+
// video, just like the server-side Sharp pipeline scales images — capped at
|
|
104
|
+
// an absolute max so a large player (hero banner, carousel) doesn't render
|
|
105
|
+
// an oversized mark just because the percentage of a big box is a big
|
|
106
|
+
// number. Mirrors MAX_WATERMARK_PX in src/app/api/media/_watermark.ts.
|
|
100
107
|
const containerStyle = {
|
|
101
108
|
position: "absolute",
|
|
102
109
|
width: `${widthPct}%`,
|
|
110
|
+
maxWidth: "180px",
|
|
103
111
|
pointerEvents: "none",
|
|
104
112
|
opacity,
|
|
105
113
|
...resolveWatermarkPlacementStyle(config),
|
|
@@ -550,14 +550,14 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
550
550
|
productId: string;
|
|
551
551
|
quantity: number;
|
|
552
552
|
productTitle: string;
|
|
553
|
+
userName: string;
|
|
553
554
|
userEmail: string;
|
|
555
|
+
unitPrice: number;
|
|
556
|
+
totalPrice: number;
|
|
554
557
|
paymentStatus: "paid" | "failed" | "pending" | "processing" | "refunded" | "partial_refund";
|
|
555
558
|
orderDate: string | Date | z.objectOutputType<{
|
|
556
559
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
557
560
|
}, z.ZodTypeAny, "passthrough">;
|
|
558
|
-
userName: string;
|
|
559
|
-
unitPrice: number;
|
|
560
|
-
totalPrice: number;
|
|
561
561
|
storeId?: string | undefined;
|
|
562
562
|
platformFee?: number | undefined;
|
|
563
563
|
paymentMethod?: string | undefined;
|
|
@@ -569,6 +569,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
569
569
|
shelf: string;
|
|
570
570
|
bin: string;
|
|
571
571
|
} | undefined;
|
|
572
|
+
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
572
573
|
items?: {
|
|
573
574
|
productId: string;
|
|
574
575
|
quantity: number;
|
|
@@ -578,11 +579,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
578
579
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
579
580
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
580
581
|
}[] | undefined;
|
|
581
|
-
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
582
|
-
couponCode?: string | undefined;
|
|
583
|
-
shippingMethod?: "custom" | undefined;
|
|
584
|
-
orderType?: string | undefined;
|
|
585
|
-
imageUrls?: string[] | undefined;
|
|
586
582
|
paymentId?: string | undefined;
|
|
587
583
|
shippingAddress?: string | undefined;
|
|
588
584
|
trackingNumber?: string | undefined;
|
|
@@ -598,6 +594,26 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
598
594
|
cancellationReason?: string | undefined;
|
|
599
595
|
refundAmount?: number | undefined;
|
|
600
596
|
refundStatus?: "pending" | "processing" | "rejected" | "completed" | undefined;
|
|
597
|
+
depositAmount?: number | undefined;
|
|
598
|
+
codRemainingAmount?: number | undefined;
|
|
599
|
+
shippingFee?: number | undefined;
|
|
600
|
+
payoutId?: string | undefined;
|
|
601
|
+
shippingMethod?: "custom" | undefined;
|
|
602
|
+
shippingCarrier?: string | undefined;
|
|
603
|
+
trackingUrl?: string | undefined;
|
|
604
|
+
bundleId?: string | undefined;
|
|
605
|
+
prizeWon?: {
|
|
606
|
+
title: string;
|
|
607
|
+
images: string[];
|
|
608
|
+
itemNumber: number;
|
|
609
|
+
wonAt: string | Date | z.objectOutputType<{
|
|
610
|
+
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
611
|
+
}, z.ZodTypeAny, "passthrough">;
|
|
612
|
+
} | undefined;
|
|
613
|
+
prizeDrawProductId?: string | undefined;
|
|
614
|
+
couponCode?: string | undefined;
|
|
615
|
+
orderType?: string | undefined;
|
|
616
|
+
imageUrls?: string[] | undefined;
|
|
601
617
|
refundFeeDeducted?: number | undefined;
|
|
602
618
|
refundNetAmount?: number | undefined;
|
|
603
619
|
refundNote?: string | undefined;
|
|
@@ -610,8 +626,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
610
626
|
storeId?: string | undefined;
|
|
611
627
|
couponId?: string | undefined;
|
|
612
628
|
}[] | undefined;
|
|
613
|
-
depositAmount?: number | undefined;
|
|
614
|
-
codRemainingAmount?: number | undefined;
|
|
615
629
|
codHandlingFee?: number | undefined;
|
|
616
630
|
emiEnabled?: boolean | undefined;
|
|
617
631
|
emiTenureMonths?: number | undefined;
|
|
@@ -635,22 +649,8 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
635
649
|
}[] | undefined;
|
|
636
650
|
emiRemainingBalance?: number | undefined;
|
|
637
651
|
emiComplete?: boolean | undefined;
|
|
638
|
-
shippingFee?: number | undefined;
|
|
639
|
-
shippingCarrier?: string | undefined;
|
|
640
|
-
trackingUrl?: string | undefined;
|
|
641
|
-
payoutId?: string | undefined;
|
|
642
652
|
offerId?: string | undefined;
|
|
643
|
-
prizeWon?: {
|
|
644
|
-
title: string;
|
|
645
|
-
images: string[];
|
|
646
|
-
itemNumber: number;
|
|
647
|
-
wonAt: string | Date | z.objectOutputType<{
|
|
648
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
649
|
-
}, z.ZodTypeAny, "passthrough">;
|
|
650
|
-
} | undefined;
|
|
651
|
-
prizeDrawProductId?: string | undefined;
|
|
652
653
|
isNonRefundable?: boolean | undefined;
|
|
653
|
-
bundleId?: string | undefined;
|
|
654
654
|
paymentBatchId?: string | undefined;
|
|
655
655
|
refunds?: {
|
|
656
656
|
type: "full" | "partial";
|
|
@@ -698,14 +698,14 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
698
698
|
productId: string;
|
|
699
699
|
quantity: number;
|
|
700
700
|
productTitle: string;
|
|
701
|
+
userName: string;
|
|
701
702
|
userEmail: string;
|
|
703
|
+
unitPrice: number;
|
|
704
|
+
totalPrice: number;
|
|
702
705
|
paymentStatus: "paid" | "failed" | "pending" | "processing" | "refunded" | "partial_refund";
|
|
703
706
|
orderDate: string | Date | z.objectInputType<{
|
|
704
707
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
705
708
|
}, z.ZodTypeAny, "passthrough">;
|
|
706
|
-
userName: string;
|
|
707
|
-
unitPrice: number;
|
|
708
|
-
totalPrice: number;
|
|
709
709
|
storeId?: string | undefined;
|
|
710
710
|
platformFee?: number | undefined;
|
|
711
711
|
paymentMethod?: string | undefined;
|
|
@@ -717,6 +717,7 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
717
717
|
shelf: string;
|
|
718
718
|
bin: string;
|
|
719
719
|
} | undefined;
|
|
720
|
+
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
720
721
|
items?: {
|
|
721
722
|
productId: string;
|
|
722
723
|
quantity: number;
|
|
@@ -726,11 +727,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
726
727
|
listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | undefined;
|
|
727
728
|
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
728
729
|
}[] | undefined;
|
|
729
|
-
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
730
|
-
couponCode?: string | undefined;
|
|
731
|
-
shippingMethod?: "custom" | undefined;
|
|
732
|
-
orderType?: string | undefined;
|
|
733
|
-
imageUrls?: string[] | undefined;
|
|
734
730
|
paymentId?: string | undefined;
|
|
735
731
|
shippingAddress?: string | undefined;
|
|
736
732
|
trackingNumber?: string | undefined;
|
|
@@ -746,6 +742,26 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
746
742
|
cancellationReason?: string | undefined;
|
|
747
743
|
refundAmount?: number | undefined;
|
|
748
744
|
refundStatus?: "pending" | "processing" | "rejected" | "completed" | undefined;
|
|
745
|
+
depositAmount?: number | undefined;
|
|
746
|
+
codRemainingAmount?: number | undefined;
|
|
747
|
+
shippingFee?: number | undefined;
|
|
748
|
+
payoutId?: string | undefined;
|
|
749
|
+
shippingMethod?: "custom" | undefined;
|
|
750
|
+
shippingCarrier?: string | undefined;
|
|
751
|
+
trackingUrl?: string | undefined;
|
|
752
|
+
bundleId?: string | undefined;
|
|
753
|
+
prizeWon?: {
|
|
754
|
+
title: string;
|
|
755
|
+
images: string[];
|
|
756
|
+
itemNumber: number;
|
|
757
|
+
wonAt: string | Date | z.objectInputType<{
|
|
758
|
+
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
759
|
+
}, z.ZodTypeAny, "passthrough">;
|
|
760
|
+
} | undefined;
|
|
761
|
+
prizeDrawProductId?: string | undefined;
|
|
762
|
+
couponCode?: string | undefined;
|
|
763
|
+
orderType?: string | undefined;
|
|
764
|
+
imageUrls?: string[] | undefined;
|
|
749
765
|
refundFeeDeducted?: number | undefined;
|
|
750
766
|
refundNetAmount?: number | undefined;
|
|
751
767
|
refundNote?: string | undefined;
|
|
@@ -758,8 +774,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
758
774
|
storeId?: string | undefined;
|
|
759
775
|
couponId?: string | undefined;
|
|
760
776
|
}[] | undefined;
|
|
761
|
-
depositAmount?: number | undefined;
|
|
762
|
-
codRemainingAmount?: number | undefined;
|
|
763
777
|
codHandlingFee?: number | undefined;
|
|
764
778
|
emiEnabled?: boolean | undefined;
|
|
765
779
|
emiTenureMonths?: number | undefined;
|
|
@@ -783,22 +797,8 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
783
797
|
}[] | undefined;
|
|
784
798
|
emiRemainingBalance?: number | undefined;
|
|
785
799
|
emiComplete?: boolean | undefined;
|
|
786
|
-
shippingFee?: number | undefined;
|
|
787
|
-
shippingCarrier?: string | undefined;
|
|
788
|
-
trackingUrl?: string | undefined;
|
|
789
|
-
payoutId?: string | undefined;
|
|
790
800
|
offerId?: string | undefined;
|
|
791
|
-
prizeWon?: {
|
|
792
|
-
title: string;
|
|
793
|
-
images: string[];
|
|
794
|
-
itemNumber: number;
|
|
795
|
-
wonAt: string | Date | z.objectInputType<{
|
|
796
|
-
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
797
|
-
}, z.ZodTypeAny, "passthrough">;
|
|
798
|
-
} | undefined;
|
|
799
|
-
prizeDrawProductId?: string | undefined;
|
|
800
801
|
isNonRefundable?: boolean | undefined;
|
|
801
|
-
bundleId?: string | undefined;
|
|
802
802
|
paymentBatchId?: string | undefined;
|
|
803
803
|
refunds?: {
|
|
804
804
|
type: "full" | "partial";
|
|
@@ -979,11 +979,11 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
979
979
|
createdAt?: string | undefined;
|
|
980
980
|
updatedAt?: string | undefined;
|
|
981
981
|
notes?: string | undefined;
|
|
982
|
-
couponCode?: string | undefined;
|
|
983
|
-
tax?: number | undefined;
|
|
984
982
|
trackingNumber?: string | undefined;
|
|
985
983
|
shippingCarrier?: string | undefined;
|
|
984
|
+
couponCode?: string | undefined;
|
|
986
985
|
discount?: number | undefined;
|
|
986
|
+
tax?: number | undefined;
|
|
987
987
|
paymentGateway?: string | undefined;
|
|
988
988
|
shippingCost?: number | undefined;
|
|
989
989
|
timeline?: {
|
|
@@ -1016,11 +1016,11 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
1016
1016
|
createdAt?: string | undefined;
|
|
1017
1017
|
updatedAt?: string | undefined;
|
|
1018
1018
|
notes?: string | undefined;
|
|
1019
|
-
couponCode?: string | undefined;
|
|
1020
|
-
tax?: number | undefined;
|
|
1021
1019
|
trackingNumber?: string | undefined;
|
|
1022
1020
|
shippingCarrier?: string | undefined;
|
|
1021
|
+
couponCode?: string | undefined;
|
|
1023
1022
|
discount?: number | undefined;
|
|
1023
|
+
tax?: number | undefined;
|
|
1024
1024
|
paymentGateway?: string | undefined;
|
|
1025
1025
|
shippingCost?: number | undefined;
|
|
1026
1026
|
timeline?: {
|
|
@@ -36,6 +36,15 @@ export interface CompareProductLike {
|
|
|
36
36
|
/** Canonical discriminator (SB1-G Phase 4). */
|
|
37
37
|
listingType?: ListingType;
|
|
38
38
|
features?: string[];
|
|
39
|
+
/** Seller-defined spec fields (dimensions, material, edition, etc.) — the
|
|
40
|
+
* per-listing-type field *set* varies, so the overlay unions the keys
|
|
41
|
+
* present across all compared items rather than assuming a fixed schema. */
|
|
42
|
+
customFields?: {
|
|
43
|
+
key: string;
|
|
44
|
+
type: string;
|
|
45
|
+
value: string;
|
|
46
|
+
unit?: string;
|
|
47
|
+
}[];
|
|
39
48
|
}
|
|
40
49
|
interface CompareFieldLabels {
|
|
41
50
|
image: string;
|
|
@@ -56,10 +56,18 @@ function priceLabel(item) {
|
|
|
56
56
|
function FieldRow({ label, children }) {
|
|
57
57
|
return (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: FIELD_LABEL_CLASS, children: label }), _jsx(Div, { children: children })] }));
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
/** Formats a single custom-field value for display, appending its unit when present. */
|
|
60
|
+
function customFieldValue(field) {
|
|
61
|
+
if (!field)
|
|
62
|
+
return "—";
|
|
63
|
+
if (field.type === "boolean")
|
|
64
|
+
return field.value === "true" ? "Yes" : "No";
|
|
65
|
+
return field.unit ? `${field.value} ${field.unit}` : field.value;
|
|
66
|
+
}
|
|
67
|
+
function CompareColumn({ item, productType, labels, onRemove, onClose, customFieldKeys }) {
|
|
60
68
|
const href = detailHref(item, productType);
|
|
61
69
|
const img = item.mainImage ?? item.images?.[0];
|
|
62
|
-
return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-[length:var(--appkit-text-sm)] font-semibold text-[var(--appkit-color-text)] hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx(IconButton, { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, variant: "ghost", rounded: "full", className: CLS_REMOVE_BTN, children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: `relative aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: img ? (_jsx(MediaImage, { src: img, alt: item.title ?? item.id, size: "card" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { size: "base", weight: "semibold", color: "primary", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx(Span, { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx(Span, { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) || (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) ? (_jsx(Span, { className: CHIP_CLASS, children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) ?? (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { size: "sm", color: "muted", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
|
|
70
|
+
return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-[length:var(--appkit-text-sm)] font-semibold text-[var(--appkit-color-text)] hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx(IconButton, { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, variant: "ghost", rounded: "full", className: CLS_REMOVE_BTN, children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: `relative aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: img ? (_jsx(MediaImage, { src: img, alt: item.title ?? item.id, size: "card" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { size: "base", weight: "semibold", color: "primary", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx(Span, { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx(Span, { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) || (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) ? (_jsx(Span, { className: CHIP_CLASS, children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) ?? (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { size: "sm", color: "muted", children: item.storeName ?? "—" }) }), customFieldKeys.map((key) => (_jsx(FieldRow, { label: key, children: _jsx(Text, { size: "sm", children: customFieldValue(item.customFields?.find((f) => f.key === key)) }) }, key))), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
|
|
63
71
|
}
|
|
64
72
|
function useFetchedProducts(productIds, passedItems, isOpen) {
|
|
65
73
|
const [fetched, setFetched] = useState([]);
|
|
@@ -109,6 +117,23 @@ export function CompareOverlay({ isOpen, items: passedItems, productIds, product
|
|
|
109
117
|
const { items, isLoading } = useFetchedProducts(productIds, passedItems, isOpen);
|
|
110
118
|
const trimmed = useMemo(() => items.slice(0, COMPARE_MAX_ITEMS), [items]);
|
|
111
119
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
120
|
+
// Union (not intersection) of custom-field keys across every compared
|
|
121
|
+
// item, first-seen order — a field only one item has still gets its own
|
|
122
|
+
// row, showing "—" for items that lack it, rather than silently dropping
|
|
123
|
+
// fields that aren't shared by every compared listing type.
|
|
124
|
+
const customFieldKeys = useMemo(() => {
|
|
125
|
+
const seen = new Set();
|
|
126
|
+
const keys = [];
|
|
127
|
+
for (const item of trimmed) {
|
|
128
|
+
for (const field of item.customFields ?? []) {
|
|
129
|
+
if (!seen.has(field.key)) {
|
|
130
|
+
seen.add(field.key);
|
|
131
|
+
keys.push(field.key);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return keys;
|
|
136
|
+
}, [trimmed]);
|
|
112
137
|
// Keep activeIndex in range as items shrink (e.g. user removes a column).
|
|
113
138
|
useEffect(() => {
|
|
114
139
|
if (activeIndex >= trimmed.length && trimmed.length > 0) {
|
|
@@ -146,7 +171,7 @@ export function CompareOverlay({ isOpen, items: passedItems, productIds, product
|
|
|
146
171
|
// audit-content-alignment-ok: loading-state text inside a transient overlay panel, not marketing content
|
|
147
172
|
_jsx(Text, { paddingY: "3xl", variant: "secondary", align: "center", children: "Loading\u00E2\u20AC\u00A6" })) : trimmed.length === 0 ? (
|
|
148
173
|
// audit-content-alignment-ok: empty-state inside a transient overlay panel, not marketing content
|
|
149
|
-
_jsx(Text, { paddingY: "3xl", variant: "secondary", align: "center", children: labels.empty })) : (_jsxs(_Fragment, { children: [_jsx(Div, { ref: gridRef, gap: "4", className: "hidden md:grid", children: trimmed.map((item) => (_jsx(CompareColumn, { item: item, productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose }, item.id))) }), _jsxs(Div, { className: "md:hidden", children: [_jsx(Div, { ref: swipeRef, className: "touch-pan-y", children: trimmed[activeIndex] && (_jsx(CompareColumn, { item: trimmed[activeIndex], productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose })) }), trimmed.length > 1 && (_jsx(Row, { justify: "center", gap: "xs", className: "mt-4", children: trimmed.map((it, idx) => (_jsx(Button, { variant: "ghost", type: "button", "aria-label": `Show item ${idx + 1}`, onClick: () => setActiveIndex(idx), rounded: "full", paddingX: "none", paddingY: "none", className: `h-2 w-2 min-w-0 transition-colors ${idx === activeIndex
|
|
174
|
+
_jsx(Text, { paddingY: "3xl", variant: "secondary", align: "center", children: labels.empty })) : (_jsxs(_Fragment, { children: [_jsx(Div, { ref: gridRef, gap: "4", className: "hidden md:grid", children: trimmed.map((item) => (_jsx(CompareColumn, { item: item, productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose, customFieldKeys: customFieldKeys }, item.id))) }), _jsxs(Div, { className: "md:hidden", children: [_jsx(Div, { ref: swipeRef, className: "touch-pan-y", children: trimmed[activeIndex] && (_jsx(CompareColumn, { item: trimmed[activeIndex], productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose, customFieldKeys: customFieldKeys })) }), trimmed.length > 1 && (_jsx(Row, { justify: "center", gap: "xs", className: "mt-4", children: trimmed.map((it, idx) => (_jsx(Button, { variant: "ghost", type: "button", "aria-label": `Show item ${idx + 1}`, onClick: () => setActiveIndex(idx), rounded: "full", paddingX: "none", paddingY: "none", className: `h-2 w-2 min-w-0 transition-colors ${idx === activeIndex
|
|
150
175
|
? "bg-primary"
|
|
151
176
|
: "bg-[var(--appkit-color-border)]"}` }, it.id))) }))] })] })) })] }));
|
|
152
177
|
}
|
|
@@ -3,10 +3,16 @@ import Link from "next/link";
|
|
|
3
3
|
import { ProductCard } from "./ProductGrid";
|
|
4
4
|
export function InteractiveProductCard({ product, href, className, selectable, isSelected, onSelect, isWishlisted = false, onToggleWishlist, onAddToCart, onBuyNow, }) {
|
|
5
5
|
// When selection is enabled, render ProductCard directly so its built-in
|
|
6
|
-
// checkbox + long-press (BaseListingCard.Checkbox) work
|
|
7
|
-
//
|
|
6
|
+
// checkbox + long-press (BaseListingCard.Checkbox) work. `href` is still
|
|
7
|
+
// forwarded here (not just in the plain-Link branch below) — ProductCard's
|
|
8
|
+
// own `if (href && !selectionMode) return <Link>...` only wraps in a Link
|
|
9
|
+
// when nothing is actively selected, so passing href unconditionally keeps
|
|
10
|
+
// the card navigable whenever a selection handler is merely wired but not
|
|
11
|
+
// active, instead of silently losing all click/tap navigation the instant
|
|
12
|
+
// `onSelect` is provided at all (see Root Cause Pattern: selectable-card
|
|
13
|
+
// navigation must not depend on a selection callback being present).
|
|
8
14
|
if (onSelect) {
|
|
9
|
-
return (_jsx(ProductCard, { product: product, className: className, isWishlisted: isWishlisted, onAddToWishlist: onToggleWishlist, onAddToCart: onAddToCart, onBuyNow: onBuyNow, selectionMode: selectable, isSelected: isSelected, onSelect: (id) => onSelect(id, !isSelected) }));
|
|
15
|
+
return (_jsx(ProductCard, { product: product, href: href, className: className, isWishlisted: isWishlisted, onAddToWishlist: onToggleWishlist, onAddToCart: onAddToCart, onBuyNow: onBuyNow, selectionMode: selectable, isSelected: isSelected, onSelect: (id) => onSelect(id, !isSelected) }));
|
|
10
16
|
}
|
|
11
17
|
return (_jsx(Link, { href: href, className: "block", children: _jsx(ProductCard, { product: product, className: className, isWishlisted: isWishlisted, onAddToWishlist: onToggleWishlist, onAddToCart: onAddToCart, onBuyNow: onBuyNow }) }));
|
|
12
18
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack } from "@mohasinac/appkit";
|
|
2
|
+
import { Stack } from "@mohasinac/appkit/client";
|
|
3
3
|
export function MakeOfferForm({ isSubmitted = false, renderInput, renderAction, renderTerms, renderSuccess, renderAuthGate, renderError, className = "", }) {
|
|
4
4
|
if (renderAuthGate)
|
|
5
5
|
return _jsx(_Fragment, { children: renderAuthGate() });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack } from "@mohasinac/appkit";
|
|
2
|
+
import { Stack } from "@mohasinac/appkit/client";
|
|
3
3
|
export function PlaceBidForm({ renderInput, renderAction, renderBidInfo, renderError, renderAuthGate, renderCountdown, className = "", }) {
|
|
4
4
|
if (renderAuthGate)
|
|
5
5
|
return _jsx(_Fragment, { children: renderAuthGate() });
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
14
14
|
* Optional `highlightItemNumber` (passed by the reveal modal during animation)
|
|
15
15
|
* adds an emphasis ring to the cell that just won.
|
|
16
16
|
*/
|
|
17
|
-
import { Row } from "@mohasinac/appkit";
|
|
17
|
+
import { Row } from "@mohasinac/appkit/client";
|
|
18
18
|
import { useState } from "react";
|
|
19
19
|
import { Button, Div, Scrim, Text } from "../../../ui";
|
|
20
20
|
import { MediaImage } from "../../media/MediaImage";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { normalizeError } from "../../../errors/normalize";
|
|
3
|
-
import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
|
-
import { sortBy } from "@mohasinac/appkit";
|
|
3
|
+
import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
4
|
+
import { sortBy } from "@mohasinac/appkit/client";
|
|
5
5
|
import { Container, Div, Heading, Section, Stack, Text, } from "../../../ui";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
7
|
import { productRepository } from "../repository/products.repository";
|
|
@@ -25,8 +25,10 @@ export function ProductDetailActions({ productId, productSlug: _productSlug, pro
|
|
|
25
25
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
26
26
|
const [busy, setBusy] = useState(null);
|
|
27
27
|
const [wishlisted, setWishlisted] = useState(false);
|
|
28
|
+
// Success toast (item + updated count/total) is now shown by useAddToCart
|
|
29
|
+
// itself — centralised so every add-to-cart call site gets the same
|
|
30
|
+
// richer toast, not just this one.
|
|
28
31
|
const addCart = useAddToCart({
|
|
29
|
-
onSuccess: () => showToast("Added to cart", "success"),
|
|
30
32
|
onError: (err) => showToast(err?.message ?? "Could not add to cart", "error"),
|
|
31
33
|
});
|
|
32
34
|
async function handleAddToCart(then) {
|
|
@@ -23,7 +23,7 @@ const CLS_BADGE_NEW = "rounded-full bg-error-surface px-[var(--appkit-space-2)]
|
|
|
23
23
|
const CLS_BADGE_SALE = "rounded-full bg-success-surface px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-white shadow-sm";
|
|
24
24
|
const CLS_BADGE_TRENDING = "rounded-full bg-indigo-600 px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-white shadow-sm";
|
|
25
25
|
const CLS_BADGE_LIMITED = "rounded-full bg-info-surface px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[10px] font-bold text-white shadow-sm";
|
|
26
|
-
const CLS_HEART_ACTIVE = "bg-
|
|
26
|
+
const CLS_HEART_ACTIVE = "bg-[var(--appkit-color-surface-elevated)]/90 text-error hover:bg-[var(--appkit-color-surface-elevated)]";
|
|
27
27
|
const CLS_HEART_IDLE = "bg-[var(--appkit-color-surface-elevated)]/90 text-[var(--appkit-color-text-muted)] hover:text-error hover:bg-[var(--appkit-color-surface-elevated)]";
|
|
28
28
|
const CLS_STAR = "text-[11px] text-warning";
|
|
29
29
|
const CLS_DISCOUNT_TEXT = "mt-1 text-[11px] font-semibold text-error";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack } from "@mohasinac/appkit";
|
|
2
|
+
import { Stack } from "@mohasinac/appkit/client";
|
|
3
3
|
export function ProductInfo({ isLoading = false, renderTitle, renderPrice, renderBadges, renderDescription, renderVariants, renderSeller, renderRating, className = "", }) {
|
|
4
4
|
return (_jsxs(Stack, { className: `${className}`, gap: "md", children: [renderTitle?.(isLoading), renderRating?.(), renderPrice?.(isLoading), renderBadges?.(), renderVariants?.(), renderDescription?.(), renderSeller?.()] }));
|
|
5
5
|
}
|
|
@@ -14,8 +14,11 @@ import { ROUTES } from "../../../next";
|
|
|
14
14
|
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
15
15
|
import { ProductGrid, ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from ".";
|
|
16
16
|
import { useGuestCart } from "../../cart/hooks/useGuestCart";
|
|
17
|
+
import { getGuestCartItems } from "../../cart/utils/guest-cart";
|
|
17
18
|
import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
18
|
-
import { pushCartOp, pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
19
|
+
import { pushCartOp, pushWishlistOp, dispatchCartUpdated, formatCartAddedMessage } from "../../cart/utils/pending-ops";
|
|
20
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
21
|
+
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
19
22
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
20
23
|
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
21
24
|
import { useBrands } from "../hooks/useBrands";
|
|
@@ -126,7 +129,14 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
|
|
|
126
129
|
};
|
|
127
130
|
localCart.add(product.id, 1, snapshot);
|
|
128
131
|
pushCartOp({ op: "add", productId: product.id, quantity: 1, ...snapshot });
|
|
129
|
-
|
|
132
|
+
const items = getGuestCartItems();
|
|
133
|
+
const detail = {
|
|
134
|
+
productTitle: product.title,
|
|
135
|
+
itemCount: items.reduce((sum, it) => sum + it.quantity, 0),
|
|
136
|
+
totalValue: items.reduce((sum, it) => sum + (it.price ?? 0) * it.quantity, 0),
|
|
137
|
+
};
|
|
138
|
+
showToast(formatCartAddedMessage(detail, (amount) => formatCurrency(amount, getDefaultCurrency())), "success");
|
|
139
|
+
dispatchCartUpdated(detail);
|
|
130
140
|
}, [localCart, showToast]);
|
|
131
141
|
const handleBuyNow = useCallback((product) => {
|
|
132
142
|
requireAuth(ACTION_ID.BUY_NOW, () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sieveAnd, sieveFilter, SIEVE_OP, sortBy } from "@mohasinac/appkit";
|
|
1
|
+
import { sieveAnd, sieveFilter, SIEVE_OP, sortBy } from "@mohasinac/appkit/client";
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { apiClient } from "../../../http";
|
|
4
4
|
import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -449,16 +449,16 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
449
449
|
inStock?: boolean | undefined;
|
|
450
450
|
rating?: number | undefined;
|
|
451
451
|
categorySlug?: string | undefined;
|
|
452
|
-
|
|
453
|
-
sellerAvatar?: string | undefined;
|
|
454
|
-
stockCount?: number | undefined;
|
|
455
|
-
attributes?: Record<string, string> | undefined;
|
|
452
|
+
publishedAt?: string | undefined;
|
|
456
453
|
seo?: {
|
|
457
454
|
title?: string | undefined;
|
|
458
455
|
description?: string | undefined;
|
|
459
456
|
keywords?: string[] | undefined;
|
|
460
457
|
} | undefined;
|
|
461
|
-
|
|
458
|
+
attributes?: Record<string, string> | undefined;
|
|
459
|
+
originalPrice?: number | undefined;
|
|
460
|
+
sellerAvatar?: string | undefined;
|
|
461
|
+
stockCount?: number | undefined;
|
|
462
462
|
}, {
|
|
463
463
|
status: "draft" | "published" | "archived" | "in_review";
|
|
464
464
|
id: string;
|
|
@@ -587,16 +587,16 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
587
587
|
inStock?: boolean | undefined;
|
|
588
588
|
rating?: number | undefined;
|
|
589
589
|
categorySlug?: string | undefined;
|
|
590
|
-
|
|
591
|
-
sellerAvatar?: string | undefined;
|
|
592
|
-
stockCount?: number | undefined;
|
|
593
|
-
attributes?: Record<string, string> | undefined;
|
|
590
|
+
publishedAt?: string | undefined;
|
|
594
591
|
seo?: {
|
|
595
592
|
title?: string | undefined;
|
|
596
593
|
description?: string | undefined;
|
|
597
594
|
keywords?: string[] | undefined;
|
|
598
595
|
} | undefined;
|
|
599
|
-
|
|
596
|
+
attributes?: Record<string, string> | undefined;
|
|
597
|
+
originalPrice?: number | undefined;
|
|
598
|
+
sellerAvatar?: string | undefined;
|
|
599
|
+
stockCount?: number | undefined;
|
|
600
600
|
}>;
|
|
601
601
|
/** Base Zod schema for list-query parameters. */
|
|
602
602
|
export declare const productListParamsSchema: z.ZodObject<{
|