@mohasinac/appkit 2.7.16 → 2.7.18
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/server/features/auctions/actions.js +1 -1
- package/dist/_internal/server/features/blog/actions.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +2 -2
- package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
- package/dist/_internal/server/features/bundles/actions.js +48 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -0
- package/dist/_internal/server/features/bundles/index.js +1 -0
- package/dist/_internal/server/features/cart/actions.js +3 -3
- package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
- package/dist/_internal/server/features/checkout/actions.js +23 -12
- package/dist/_internal/server/features/events/actions.js +3 -3
- package/dist/_internal/server/features/orders/actions.js +4 -4
- package/dist/_internal/server/features/payments/data.d.ts +5 -3
- package/dist/_internal/server/features/payments/data.js +13 -8
- package/dist/_internal/server/features/payments/index.d.ts +1 -1
- package/dist/_internal/server/features/payments/index.js +1 -1
- package/dist/_internal/server/features/pre-orders/actions.js +1 -1
- package/dist/_internal/server/features/products/actions.js +6 -6
- package/dist/_internal/server/features/promotions/actions.js +3 -3
- package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
- package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
- package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
- package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
- package/dist/_internal/shared/features/checkout/config.js +1 -1
- package/dist/_internal/shared/features/payments/config.d.ts +2 -1
- package/dist/_internal/shared/features/payments/config.js +2 -1
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/constants/field-names.d.ts +8 -1
- package/dist/constants/field-names.js +9 -2
- package/dist/constants/table-keys.d.ts +3 -0
- package/dist/constants/table-keys.js +3 -0
- package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
- package/dist/features/admin/components/DashboardStats.js +20 -12
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
- package/dist/features/admin/schemas/firestore.d.ts +18 -4
- package/dist/features/admin/schemas/firestore.js +11 -1
- package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
- package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
- package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
- package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
- package/dist/features/categories/components/BundlesListView.d.ts +4 -1
- package/dist/features/categories/components/BundlesListView.js +2 -2
- package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
- package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
- package/dist/features/categories/schemas/firestore.d.ts +29 -1
- package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
- package/dist/features/checkout/actions/checkout-actions.js +26 -0
- package/dist/features/filters/FilterFacetSection.d.ts +6 -1
- package/dist/features/filters/FilterFacetSection.js +25 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
- package/dist/features/orders/actions/refund-actions.js +10 -7
- package/dist/features/orders/schemas/firestore.d.ts +16 -13
- package/dist/features/orders/schemas/firestore.js +6 -5
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
- package/dist/features/products/components/AuctionsIndexListing.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.js +3 -1
- package/dist/features/products/components/ProductDetailView.d.ts +3 -1
- package/dist/features/products/components/ProductDetailView.js +2 -2
- package/dist/features/products/components/ProductFilters.d.ts +10 -4
- package/dist/features/products/components/ProductFilters.js +20 -16
- package/dist/features/products/components/ProductGrid.js +3 -3
- package/dist/features/products/components/ProductsIndexListing.js +2 -1
- package/dist/features/products/constants/product-features.constants.js +5 -1
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.d.ts +1 -1
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/products/schemas/product-features.validators.js +4 -0
- package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
- package/dist/features/shell/FormShell.js +1 -1
- package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +2 -1
- package/dist/features/wishlist/hooks/useWishlistCount.d.ts +2 -2
- package/dist/features/wishlist/hooks/useWishlistCount.js +5 -8
- package/dist/providers/db-firebase/admin-app-lite.js +8 -2
- package/dist/providers/db-firebase/admin.js +8 -2
- package/dist/seed/addresses-seed-data.js +24 -7
- package/dist/seed/cart-seed-data.d.ts +10 -10
- package/dist/seed/cart-seed-data.js +39 -2
- package/dist/seed/grouped-listings-seed-data.js +47 -0
- package/dist/seed/orders-seed-data.js +150 -0
- package/dist/seed/products-standard-seed-data.js +42 -0
- package/dist/seed/site-settings-seed-data.js +11 -4
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seed/wishlists-seed-data.js +7 -0
- package/dist/server-entry.d.ts +2 -1
- package/dist/server-entry.js +2 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +3 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Breadcrumb.style.css +1 -1
- package/dist/ui/components/ListingLayout.style.css +1 -1
- package/dist/ui/components/Select.style.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +55 -12
- package/dist/ui/components/Typography.style.css +2 -2
- package/dist/ui/rich-text/RichText.style.css +4 -5
- package/package.json +1 -1
|
@@ -49,7 +49,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
49
49
|
.filter(Boolean)
|
|
50
50
|
.join(" "), children: [_jsxs(Div, { className: "relative overflow-hidden bg-zinc-100 dark:bg-slate-800 aspect-square", children: [product.mainImage ? (_jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", className: "transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Div, { className: "h-full w-full flex items-center justify-center bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700", children: _jsx(Span, { className: "text-4xl opacity-30", children: "\uD83D\uDECD\uFE0F" }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(product.id); }, label: isSelected ? "Deselect" : "Select", position: "top-2 left-2", className: selectionMode || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Div, { className: `absolute top-2 flex flex-col gap-1 ${onSelect ? "left-10" : "left-2"}`, children: !selectionMode && !isSelected && (_jsxs(_Fragment, { children: [discount && (_jsxs(Span, { className: "rounded-full bg-rose-500 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", children: ["-", discount, "%"] })), typeBadge && (_jsx(Span, { className: `rounded-full px-2 py-0.5 text-[10px] font-bold shadow-sm ${typeBadge.cls}`, children: typeBadge.label })), product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: "rounded-full bg-teal-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.partOfBundleTitles && product.partOfBundleTitles.length > 0
|
|
51
51
|
? `In bundle: ${product.partOfBundleTitles[0]}`
|
|
52
|
-
: "In a bundle", children: "Bundled" }))] })) }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
52
|
+
: "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: "rounded-full bg-sky-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm", title: "Has variants or sub-listings", children: "Has Variants" }))] })) }), onAddToWishlist && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: (e) => {
|
|
53
53
|
e.stopPropagation();
|
|
54
54
|
e.preventDefault();
|
|
55
55
|
onAddToWishlist(product.id);
|
|
@@ -59,7 +59,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
59
59
|
isWishlisted
|
|
60
60
|
? "bg-rose-500 text-white hover:bg-rose-600"
|
|
61
61
|
: "bg-white/90 dark:bg-slate-800/90 text-zinc-500 dark:text-zinc-400 hover:text-rose-500 hover:bg-white dark:hover:bg-slate-800",
|
|
62
|
-
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (product.categoryName || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [product.categoryName && (
|
|
62
|
+
].join(" "), children: _jsx("svg", { className: "h-4 w-4", fill: isWishlisted ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" }) }) }))] }), _jsxs(Div, { className: "flex flex-1 flex-col p-3 pt-2.5", children: [_jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-900 dark:text-white leading-snug`, children: product.title }), (product.categoryName || product.brand) && (_jsxs(Row, { className: "mt-1 gap-1 flex-wrap", children: [product.categoryName && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[100px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 7a2 2 0 012-2h14a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" }) }), product.categoryName] })), product.brand && (_jsxs(Span, { className: "inline-flex items-center gap-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:text-zinc-400 truncate max-w-[90px]", children: [_jsx("svg", { className: "h-2.5 w-2.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), product.brand] }))] })), (() => {
|
|
63
63
|
const seller = safeDisplayName(product.storeName, "");
|
|
64
64
|
return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px] text-zinc-400 dark:text-zinc-500 truncate", children: ["by ", seller] })) : null;
|
|
65
65
|
})(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1 gap-1 items-center", children: [_jsx(Span, { className: "text-[11px] text-amber-400", children: "\u2605" }), _jsxs(Span, { className: "text-[11px] text-zinc-500 dark:text-zinc-400", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto pt-2", children: [_jsxs(Row, { className: "items-baseline gap-2", children: [_jsx(Span, { className: "text-base font-bold text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { className: "text-xs text-zinc-400 line-through dark:text-zinc-500", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
|
|
@@ -81,7 +81,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
81
81
|
e.stopPropagation();
|
|
82
82
|
e.preventDefault();
|
|
83
83
|
onAddToCart(product);
|
|
84
|
-
}, className: "flex items-center justify-center gap-1 rounded-xl border-2 border-primary/
|
|
84
|
+
}, className: "flex items-center justify-center gap-1 rounded-xl border-2 border-primary/40 bg-primary/5 py-2 text-xs font-semibold text-primary hover:bg-primary/10 hover:border-primary/60 active:scale-[0.97] transition-all duration-150 dark:text-primary-400 dark:border-primary-400/50 dark:bg-primary/[0.08] dark:hover:bg-primary/[0.15] dark:hover:border-primary-400/70", children: [_jsx("svg", { className: "h-3.5 w-3.5 flex-shrink-0", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 3h2l.4 2M7 13h10l4-8H5.4m1.6 8L5 3H3m4 10v7a1 1 0 001 1h8a1 1 0 001-1v-7M9 21h6" }) }), "Cart"] }))] }))] })] })] }));
|
|
85
85
|
if (href && !selectionMode) {
|
|
86
86
|
return (_jsx(Link, { href: href, className: "block h-full", children: cardBody }));
|
|
87
87
|
}
|
|
@@ -79,6 +79,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
79
79
|
setSearchInput("");
|
|
80
80
|
}, [table]);
|
|
81
81
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
82
|
+
const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
|
|
82
83
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
83
84
|
table.get(TABLE_KEYS.SHOW_SOLD) === "true" ||
|
|
84
85
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
@@ -196,5 +197,5 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
196
197
|
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
|
|
197
198
|
setCompareIds([]);
|
|
198
199
|
selection.clearSelection();
|
|
199
|
-
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [
|
|
200
|
+
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
|
|
200
201
|
}
|
|
@@ -18,10 +18,14 @@ export const PRODUCT_FEATURE_CATEGORY_OPTIONS = [
|
|
|
18
18
|
{ value: "custom", label: "Custom" },
|
|
19
19
|
];
|
|
20
20
|
export const PRODUCT_FEATURE_PRODUCT_TYPE_OPTIONS = [
|
|
21
|
-
{ value: "all", label: "All
|
|
21
|
+
{ value: "all", label: "All listing types" },
|
|
22
22
|
{ value: "product", label: "Standard product" },
|
|
23
23
|
{ value: "auction", label: "Auction" },
|
|
24
24
|
{ value: "preorder", label: "Pre-order" },
|
|
25
|
+
{ value: "prize-draw", label: "Prize draw" },
|
|
26
|
+
{ value: "classified", label: "Classified" },
|
|
27
|
+
{ value: "digital-code", label: "Digital code" },
|
|
28
|
+
{ value: "live", label: "Live item" },
|
|
25
29
|
];
|
|
26
30
|
export const PRODUCT_FEATURE_SCOPE_OPTIONS = [
|
|
27
31
|
{ value: "platform", label: "Platform (all stores)" },
|
|
@@ -315,6 +315,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
315
315
|
preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
|
|
316
316
|
prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
|
|
317
317
|
categorySlug?: string | undefined;
|
|
318
|
+
features?: string[] | undefined;
|
|
318
319
|
images?: string[] | undefined;
|
|
319
320
|
description?: string | undefined;
|
|
320
321
|
isPromoted?: boolean | undefined;
|
|
@@ -328,7 +329,6 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
328
329
|
name: string;
|
|
329
330
|
unit?: string | undefined;
|
|
330
331
|
}[] | undefined;
|
|
331
|
-
features?: string[] | undefined;
|
|
332
332
|
shippingInfo?: string | undefined;
|
|
333
333
|
returnPolicy?: string | undefined;
|
|
334
334
|
insurance?: boolean | undefined;
|
|
@@ -444,6 +444,7 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
444
444
|
preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
|
|
445
445
|
prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
|
|
446
446
|
categorySlug?: string | undefined;
|
|
447
|
+
features?: string[] | undefined;
|
|
447
448
|
images?: string[] | undefined;
|
|
448
449
|
description?: string | undefined;
|
|
449
450
|
isPromoted?: boolean | undefined;
|
|
@@ -457,7 +458,6 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
457
458
|
name: string;
|
|
458
459
|
unit?: string | undefined;
|
|
459
460
|
}[] | undefined;
|
|
460
|
-
features?: string[] | undefined;
|
|
461
461
|
shippingInfo?: string | undefined;
|
|
462
462
|
returnPolicy?: string | undefined;
|
|
463
463
|
insurance?: boolean | undefined;
|
|
@@ -20,7 +20,7 @@ export declare const MAX_STORE_CUSTOM_FEATURES = 20;
|
|
|
20
20
|
export declare const MAX_FEATURES_PER_PRODUCT = 10;
|
|
21
21
|
export type ProductFeatureScope = "platform" | "store";
|
|
22
22
|
export type ProductFeatureCategory = "shipping" | "seller" | "condition" | "platform" | "auction" | "preorder" | "custom";
|
|
23
|
-
export type ProductFeatureProductType = "product" | "auction" | "preorder" | "all";
|
|
23
|
+
export type ProductFeatureProductType = "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live" | "all";
|
|
24
24
|
/**
|
|
25
25
|
* Either an appkit icon-set name key (e.g. "truck") or a raw SVG path-d string.
|
|
26
26
|
* Path strings must start with "M " — see isFeatureIconPath().
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* of hand-rolling parallel schemas.
|
|
7
7
|
*/
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "all"]>;
|
|
9
|
+
export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>;
|
|
10
10
|
export declare const PRODUCT_FEATURE_CATEGORY_ENUM: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
|
|
11
11
|
export declare const PRODUCT_FEATURE_SCOPE_ENUM: z.ZodEnum<["platform", "store"]>;
|
|
12
12
|
/** Shape for POST /api/admin/features — admin creates either scope. */
|
|
@@ -17,7 +17,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
|
|
|
17
17
|
iconColor: z.ZodOptional<z.ZodString>;
|
|
18
18
|
category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
|
|
19
19
|
scope: z.ZodEnum<["platform", "store"]>;
|
|
20
|
-
productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
|
|
20
|
+
productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
|
|
21
21
|
storeId: z.ZodOptional<z.ZodString>;
|
|
22
22
|
isActive: z.ZodBoolean;
|
|
23
23
|
displayOrder: z.ZodNumber;
|
|
@@ -28,7 +28,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
|
|
|
28
28
|
isActive: boolean;
|
|
29
29
|
category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
|
|
30
30
|
displayOrder: number;
|
|
31
|
-
productTypes: ("all" | "product" | "auction" | "preorder")[];
|
|
31
|
+
productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
|
|
32
32
|
storeId?: string | undefined;
|
|
33
33
|
description?: string | undefined;
|
|
34
34
|
iconColor?: string | undefined;
|
|
@@ -39,7 +39,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
|
|
|
39
39
|
isActive: boolean;
|
|
40
40
|
category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
|
|
41
41
|
displayOrder: number;
|
|
42
|
-
productTypes: ("all" | "product" | "auction" | "preorder")[];
|
|
42
|
+
productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
|
|
43
43
|
storeId?: string | undefined;
|
|
44
44
|
description?: string | undefined;
|
|
45
45
|
iconColor?: string | undefined;
|
|
@@ -51,7 +51,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
|
|
|
51
51
|
icon: z.ZodString;
|
|
52
52
|
iconColor: z.ZodOptional<z.ZodString>;
|
|
53
53
|
category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
|
|
54
|
-
productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
|
|
54
|
+
productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
|
|
55
55
|
isActive: z.ZodBoolean;
|
|
56
56
|
displayOrder: z.ZodNumber;
|
|
57
57
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -60,7 +60,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
|
|
|
60
60
|
isActive: boolean;
|
|
61
61
|
category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
|
|
62
62
|
displayOrder: number;
|
|
63
|
-
productTypes: ("all" | "product" | "auction" | "preorder")[];
|
|
63
|
+
productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
|
|
64
64
|
description?: string | undefined;
|
|
65
65
|
iconColor?: string | undefined;
|
|
66
66
|
}, {
|
|
@@ -69,7 +69,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
|
|
|
69
69
|
isActive: boolean;
|
|
70
70
|
category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
|
|
71
71
|
displayOrder: number;
|
|
72
|
-
productTypes: ("all" | "product" | "auction" | "preorder")[];
|
|
72
|
+
productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
|
|
73
73
|
description?: string | undefined;
|
|
74
74
|
iconColor?: string | undefined;
|
|
75
75
|
}>;
|
|
@@ -80,7 +80,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
|
|
|
80
80
|
icon: z.ZodOptional<z.ZodString>;
|
|
81
81
|
iconColor: z.ZodOptional<z.ZodString>;
|
|
82
82
|
category: z.ZodOptional<z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>>;
|
|
83
|
-
productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">>;
|
|
83
|
+
productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">>;
|
|
84
84
|
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
85
85
|
displayOrder: z.ZodOptional<z.ZodNumber>;
|
|
86
86
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -91,7 +91,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
|
|
|
91
91
|
description?: string | undefined;
|
|
92
92
|
iconColor?: string | undefined;
|
|
93
93
|
displayOrder?: number | undefined;
|
|
94
|
-
productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
|
|
94
|
+
productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
|
|
95
95
|
}, {
|
|
96
96
|
label?: string | undefined;
|
|
97
97
|
icon?: string | undefined;
|
|
@@ -100,7 +100,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
|
|
|
100
100
|
description?: string | undefined;
|
|
101
101
|
iconColor?: string | undefined;
|
|
102
102
|
displayOrder?: number | undefined;
|
|
103
|
-
productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
|
|
103
|
+
productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
|
|
104
104
|
}>;
|
|
105
105
|
export type ProductFeatureAdminCreatePayload = z.infer<typeof productFeatureAdminCreateSchema>;
|
|
106
106
|
export type ProductFeatureStoreCreatePayload = z.infer<typeof productFeatureStoreCreateSchema>;
|
|
@@ -61,7 +61,7 @@ export function SellerCouponEditorView({ couponId, initial, onSave, onCancel, })
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
const isEdit = Boolean(couponId);
|
|
64
|
-
return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto", children: [_jsx(
|
|
64
|
+
return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] shadow-sm overflow-hidden", children: [_jsx("div", { className: "h-[3px] w-full", style: { background: "linear-gradient(to right,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)" }, "aria-hidden": "true" }), _jsx(Div, { className: "border-b border-[var(--appkit-color-border)] px-6 py-5", children: _jsxs(Row, { className: "items-center justify-between gap-3", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-[var(--appkit-color-text)]", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", className: "px-6 py-6", children: [error && (_jsx(Div, { className: "rounded-lg border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
|
|
65
65
|
? "Enter a value between 1 and 100"
|
|
66
|
-
: "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)]
|
|
66
|
+
: "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-6 py-4", children: _jsxs(Row, { className: "items-center justify-end gap-3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
|
|
67
67
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { TrendingUp, ShoppingBag, Package, CheckCircle } from "lucide-react";
|
|
4
|
+
import { Div } from "../../../../ui";
|
|
5
|
+
const GREEN_GRAD = "linear-gradient(135deg,var(--appkit-color-cobalt,#3570fc) 0%,var(--appkit-color-secondary-400,#84e122) 100%)";
|
|
6
|
+
const BRAND_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)";
|
|
7
|
+
const AMBER_GRAD = "linear-gradient(135deg,var(--appkit-color-amber-500,#f59e0b) 0%,var(--appkit-color-amber-600,#d97706) 100%)";
|
|
8
|
+
const BLUE_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 100%)";
|
|
9
|
+
function StatCard({ label, value, gradient, Icon }) {
|
|
10
|
+
return (_jsxs(Div, { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: gradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx("div", { className: "mt-2 text-2xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-none", children: value })] }), _jsx("div", { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center", style: { background: gradient }, children: _jsx(Icon, { className: "w-5 h-5 text-white" }) })] })] }));
|
|
5
11
|
}
|
|
6
12
|
export function SellerAnalyticsStats({ summary, labels = {}, formatRevenue = (amount) => String(amount), className = "", }) {
|
|
7
|
-
return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4
|
|
13
|
+
return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 ${className}`, children: [_jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatRevenue(summary.totalRevenue), gradient: GREEN_GRAD, Icon: TrendingUp }), _jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: String(summary.totalOrders), gradient: BRAND_GRAD, Icon: ShoppingBag }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: String(summary.totalProducts), gradient: AMBER_GRAD, Icon: Package }), _jsx(StatCard, { label: labels.publishedProducts ?? "Published", value: String(summary.publishedProducts), gradient: BLUE_GRAD, Icon: CheckCircle })] }));
|
|
8
14
|
}
|
|
@@ -101,5 +101,5 @@ export function FormShell({ isOpen, onClose, title, breadcrumb, isDirty = false,
|
|
|
101
101
|
};
|
|
102
102
|
if (!isOpen)
|
|
103
103
|
return null;
|
|
104
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs("div", { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed
|
|
104
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs("div", { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 bottom-0 left-0 right-0 lg:left-[10%] lg:right-[10%] flex flex-col bg-[var(--appkit-color-surface)] shadow-2xl", style: { zIndex: "var(--appkit-z-modal)" }, children: [_jsxs("div", { className: "flex-shrink-0 sticky top-0 z-10 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-4 py-3 flex items-center gap-3", children: [previewMode ? (_jsxs("button", { type: "button", onClick: () => setPreviewMode(false), "aria-label": "Back to edit", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0 flex items-center gap-1.5 text-sm", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Back to Edit" })] })) : (_jsx("button", { type: "button", onClick: attemptClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0", children: _jsx(X, { className: "w-5 h-5" }) })), _jsxs("div", { className: "flex-1 min-w-0", children: [breadcrumb && (_jsx("p", { className: "text-xs text-[var(--appkit-color-text-muted)] truncate mb-0.5", children: breadcrumb })), _jsx("p", { className: "text-sm font-semibold text-[var(--appkit-color-text)] truncate", children: previewMode ? `Preview — ${title}` : title })] }), _jsx("div", { className: "flex items-center gap-2 flex-shrink-0", children: previewMode ? null : (_jsxs(_Fragment, { children: [previewSlot && (_jsxs("button", { type: "button", onClick: () => setPreviewMode(true), className: "flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors border border-[var(--appkit-color-border)]", children: [_jsx(Eye, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Preview" })] })), onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsx(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: publishLabel }))] })) })] }), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [sections && sections.length > 0 && !previewMode && (_jsx("nav", { "aria-label": "Form sections", className: "hidden lg:flex flex-col flex-shrink-0 w-48 border-r border-[var(--appkit-color-border)] py-4 px-3 gap-1 overflow-y-auto", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "text-left text-sm px-3 py-2 rounded-lg text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: sec.label }, sec.id))) })), sections && sections.length > 0 && !previewMode && (_jsx("div", { className: "lg:hidden fixed top-[var(--form-shell-topbar-h,57px)] left-0 right-0 z-10 flex overflow-x-auto gap-1 px-4 py-2 bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "flex-shrink-0 text-xs px-3 py-1.5 rounded-full border border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] transition-colors whitespace-nowrap", children: sec.label }, sec.id))) })), _jsx("div", { ref: bodyRef, className: classNames("flex-1 overflow-y-auto", sections && sections.length > 0 && !previewMode ? "pt-0 lg:pt-0" : ""), children: previewMode && previewSlot ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sticky top-0 z-10 flex items-center gap-2 bg-[var(--appkit-color-warning-surface)] border-b border-[var(--appkit-color-warning)] px-4 py-2 text-sm text-[var(--appkit-color-warning-text,var(--appkit-color-warning))]", children: [_jsx(Eye, { className: "w-4 h-4 flex-shrink-0" }), _jsx("span", { children: "Preview \u2014 not visible to buyers until published" })] }), _jsx("div", { className: "py-4", children: previewSlot() })] })) : (_jsx("div", { className: "max-w-3xl mx-auto px-4 py-6 sm:px-6", children: children })) })] }), !previewMode && renderBottomBar ? (renderBottomBar()) : !previewMode && (onSaveDraft || onPublish) ? (_jsxs("div", { className: "flex-shrink-0 sticky bottom-0 z-10 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-4 py-3 flex items-center justify-between", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: attemptClose, disabled: isLoading, children: FORM_ACTION_META[FORM_ACTION_ID.DISCARD].label }), _jsxs("div", { className: "flex items-center gap-2", children: [onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsxs(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: [publishLabel, " \u2192"] }))] })] })) : null] }), showUnsaved && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/60", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, onClick: () => setShowUnsaved(false) }), _jsxs("div", { className: "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] max-w-sm bg-[var(--appkit-color-surface)] rounded-xl shadow-2xl p-6", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, children: [_jsxs("div", { className: "flex gap-3 mb-4", children: [_jsx("span", { className: "flex-shrink-0 w-10 h-10 rounded-full bg-[var(--appkit-color-warning-surface)] flex items-center justify-center", children: _jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--appkit-color-warning)]" }) }), _jsxs("div", { children: [_jsx("p", { className: "font-semibold text-[var(--appkit-color-text)]", children: "Unsaved changes" }), _jsx("p", { className: "text-sm text-[var(--appkit-color-text-muted)] mt-1", children: "You have unsaved changes. Leave without saving?" })] })] }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowUnsaved(false), children: "Stay" }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => { setShowUnsaved(false); onClose(); }, children: "Leave" })] })] })] }))] }));
|
|
105
105
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export interface StoreBundlesPageViewProps {
|
|
2
2
|
storeSlug: string;
|
|
3
|
+
onBuyNow?: (input: {
|
|
4
|
+
bundleSlug: string;
|
|
5
|
+
}) => Promise<unknown>;
|
|
3
6
|
}
|
|
4
7
|
/**
|
|
5
8
|
* Public store → Bundles tab.
|
|
@@ -9,4 +12,4 @@ export interface StoreBundlesPageViewProps {
|
|
|
9
12
|
* the seller's active bundle categories and hands them to
|
|
10
13
|
* `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
|
|
11
14
|
*/
|
|
12
|
-
export declare function StoreBundlesPageView({ storeSlug, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
|
|
15
|
+
export declare function StoreBundlesPageView({ storeSlug, onBuyNow, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
|
|
@@ -10,7 +10,7 @@ import { CategoryBundlesListing } from "../../categories/components/CategoryBund
|
|
|
10
10
|
* the seller's active bundle categories and hands them to
|
|
11
11
|
* `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
|
|
12
12
|
*/
|
|
13
|
-
export async function StoreBundlesPageView({ storeSlug, }) {
|
|
13
|
+
export async function StoreBundlesPageView({ storeSlug, onBuyNow, }) {
|
|
14
14
|
const store = await getStoreBySlug(storeSlug);
|
|
15
15
|
const storeId = store?.id;
|
|
16
16
|
if (!storeId) {
|
|
@@ -20,5 +20,5 @@ export async function StoreBundlesPageView({ storeSlug, }) {
|
|
|
20
20
|
.listByType("bundle", { activeOnly: true, limit: 50 })
|
|
21
21
|
.then((rows) => rows.filter((c) => c.createdByStoreId === storeId))
|
|
22
22
|
.catch(() => []);
|
|
23
|
-
return _jsx(CategoryBundlesListing, { initialBundles: bundles });
|
|
23
|
+
return _jsx(CategoryBundlesListing, { initialBundles: bundles, onBuyNow: onBuyNow });
|
|
24
24
|
}
|
|
@@ -80,6 +80,7 @@ export function StoresIndexListing({ initialData }) {
|
|
|
80
80
|
setSearchInput("");
|
|
81
81
|
}, [table]);
|
|
82
82
|
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
83
|
+
const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
|
|
83
84
|
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
84
85
|
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
85
86
|
activeFilterCount > 0;
|
|
@@ -132,5 +133,5 @@ export function StoresIndexListing({ initialData }) {
|
|
|
132
133
|
}) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: stores.map((store) => {
|
|
133
134
|
const storeKey = store.storeSlug ?? store.id;
|
|
134
135
|
return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
|
|
135
|
-
}) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [
|
|
136
|
+
}) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(StoreFilters, { table: pendingTable }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
|
|
136
137
|
}
|
|
@@ -10,8 +10,8 @@ export interface WishlistCapEventDetail {
|
|
|
10
10
|
*
|
|
11
11
|
* Server-authoritative count for the badge. For authenticated users this
|
|
12
12
|
* mirrors `/api/wishlist` via react-query (same `["wishlist", uid]` key as
|
|
13
|
-
* `useWishlist`),
|
|
14
|
-
*
|
|
13
|
+
* `useWishlist`), storing a full WishlistResponse so both hooks share the
|
|
14
|
+
* cache without type collisions. For guests, falls back to the localStorage
|
|
15
15
|
* guest store.
|
|
16
16
|
*
|
|
17
17
|
* On login the hook pushes any guest items up to the server once, then clears
|
|
@@ -15,17 +15,13 @@ function dispatchCapEvent(detail) {
|
|
|
15
15
|
return;
|
|
16
16
|
window.dispatchEvent(new CustomEvent(WISHLIST_CAP_EVENT, { detail }));
|
|
17
17
|
}
|
|
18
|
-
async function fetchServerCount(userId) {
|
|
19
|
-
const data = await apiClient.get(WISHLIST_ENDPOINTS.BY_USER(userId));
|
|
20
|
-
return data?.total ?? data?.items?.length ?? 0;
|
|
21
|
-
}
|
|
22
18
|
/**
|
|
23
19
|
* useWishlistCount
|
|
24
20
|
*
|
|
25
21
|
* Server-authoritative count for the badge. For authenticated users this
|
|
26
22
|
* mirrors `/api/wishlist` via react-query (same `["wishlist", uid]` key as
|
|
27
|
-
* `useWishlist`),
|
|
28
|
-
*
|
|
23
|
+
* `useWishlist`), storing a full WishlistResponse so both hooks share the
|
|
24
|
+
* cache without type collisions. For guests, falls back to the localStorage
|
|
29
25
|
* guest store.
|
|
30
26
|
*
|
|
31
27
|
* On login the hook pushes any guest items up to the server once, then clears
|
|
@@ -36,12 +32,13 @@ export function useWishlistCount(userId) {
|
|
|
36
32
|
const queryClient = useQueryClient();
|
|
37
33
|
const mergedRef = useRef(null);
|
|
38
34
|
const pathname = usePathname();
|
|
39
|
-
const { data:
|
|
35
|
+
const { data: wishlistData } = useQuery({
|
|
40
36
|
queryKey: ["wishlist", userId],
|
|
41
|
-
queryFn: () =>
|
|
37
|
+
queryFn: () => apiClient.get(WISHLIST_ENDPOINTS.BY_USER(userId)),
|
|
42
38
|
enabled: !!userId,
|
|
43
39
|
staleTime: 30000,
|
|
44
40
|
});
|
|
41
|
+
const serverTotal = wishlistData?.total ?? wishlistData?.items?.length ?? 0;
|
|
45
42
|
// Merge on login: push guest items → server once, then clear local store.
|
|
46
43
|
useEffect(() => {
|
|
47
44
|
if (!userId) {
|
|
@@ -24,12 +24,14 @@ export function getAdminAppLite() {
|
|
|
24
24
|
return existing;
|
|
25
25
|
}
|
|
26
26
|
const keyPath = nodePath().join(nodeCwd(), "firebase-admin-key.json");
|
|
27
|
+
const storageBucket = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
|
|
28
|
+
process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
|
|
27
29
|
let app;
|
|
28
30
|
if (nodeFs().existsSync(keyPath)) {
|
|
29
31
|
const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
|
|
30
32
|
const dbUrl = process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
|
|
31
33
|
`https://${sa.project_id}-default-rtdb.firebaseio.com`;
|
|
32
|
-
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl });
|
|
34
|
+
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
|
|
33
35
|
}
|
|
34
36
|
else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
|
|
35
37
|
process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
|
|
@@ -45,6 +47,7 @@ export function getAdminAppLite() {
|
|
|
45
47
|
privateKey: parsePrivateKey(process.env.FIREBASE_ADMIN_PRIVATE_KEY),
|
|
46
48
|
}),
|
|
47
49
|
databaseURL: dbUrl,
|
|
50
|
+
...(storageBucket && { storageBucket }),
|
|
48
51
|
});
|
|
49
52
|
}
|
|
50
53
|
else if (
|
|
@@ -66,7 +69,10 @@ export function getAdminAppLite() {
|
|
|
66
69
|
process.env.FIREBASE_ADMIN_DATABASE_URL ??
|
|
67
70
|
process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
|
|
68
71
|
(projectId ? `https://${projectId}-default-rtdb.firebaseio.com` : undefined);
|
|
69
|
-
app = initializeApp(
|
|
72
|
+
app = initializeApp({
|
|
73
|
+
...(dbUrl && { databaseURL: dbUrl }),
|
|
74
|
+
...(storageBucket && { storageBucket }),
|
|
75
|
+
});
|
|
70
76
|
}
|
|
71
77
|
else {
|
|
72
78
|
throw new Error("@mohasinac/db-firebase: Firebase Admin credentials not found.");
|
|
@@ -67,13 +67,15 @@ export function getAdminApp() {
|
|
|
67
67
|
return existing;
|
|
68
68
|
}
|
|
69
69
|
const keyPath = nodePath().join(nodeCwd(), "firebase-admin-key.json");
|
|
70
|
+
const storageBucket = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
|
|
71
|
+
process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
|
|
70
72
|
let app;
|
|
71
73
|
try {
|
|
72
74
|
if (nodeFs().existsSync(keyPath)) {
|
|
73
75
|
const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
|
|
74
76
|
const dbUrl = process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
|
|
75
77
|
`https://${sa.project_id}-default-rtdb.firebaseio.com`;
|
|
76
|
-
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl });
|
|
78
|
+
app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
|
|
77
79
|
}
|
|
78
80
|
else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
|
|
79
81
|
process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
|
|
@@ -89,6 +91,7 @@ export function getAdminApp() {
|
|
|
89
91
|
privateKey: parsePrivateKey(process.env.FIREBASE_ADMIN_PRIVATE_KEY),
|
|
90
92
|
}),
|
|
91
93
|
databaseURL: dbUrl,
|
|
94
|
+
...(storageBucket && { storageBucket }),
|
|
92
95
|
});
|
|
93
96
|
}
|
|
94
97
|
else if (
|
|
@@ -112,7 +115,10 @@ export function getAdminApp() {
|
|
|
112
115
|
(projectId
|
|
113
116
|
? `https://${projectId}-default-rtdb.firebaseio.com`
|
|
114
117
|
: undefined);
|
|
115
|
-
app = initializeApp(
|
|
118
|
+
app = initializeApp({
|
|
119
|
+
...(dbUrl && { databaseURL: dbUrl }),
|
|
120
|
+
...(storageBucket && { storageBucket }),
|
|
121
|
+
});
|
|
116
122
|
}
|
|
117
123
|
else {
|
|
118
124
|
throw new Error("@mohasinac/db-firebase: Firebase Admin credentials not found.\n" +
|
|
@@ -197,14 +197,14 @@ export const addressesSeedData = [
|
|
|
197
197
|
updatedAt: daysAgo(739),
|
|
198
198
|
},
|
|
199
199
|
// ============================================
|
|
200
|
-
// Admin User's
|
|
200
|
+
// Admin User's Addresses (user-admin-letitrip)
|
|
201
201
|
// ============================================
|
|
202
202
|
{
|
|
203
203
|
id: "addr-admin-primary-1707400017",
|
|
204
|
-
userId: "user-admin-
|
|
205
|
-
label: "
|
|
206
|
-
fullName: "Admin
|
|
207
|
-
phone: `${_phonePrefix}
|
|
204
|
+
userId: "user-admin-letitrip",
|
|
205
|
+
label: "Office (LetItRip HQ)",
|
|
206
|
+
fullName: "LetItRip Admin",
|
|
207
|
+
phone: `${_phonePrefix}9876500000`,
|
|
208
208
|
addressLine1: "LetItRip HQ, 10th Floor",
|
|
209
209
|
addressLine2: "BKC, G Block",
|
|
210
210
|
city: "Mumbai",
|
|
@@ -212,7 +212,24 @@ export const addressesSeedData = [
|
|
|
212
212
|
postalCode: "400051",
|
|
213
213
|
country: _locale.countryName,
|
|
214
214
|
isDefault: true,
|
|
215
|
-
createdAt: daysAgo(
|
|
216
|
-
updatedAt: daysAgo(
|
|
215
|
+
createdAt: daysAgo(399),
|
|
216
|
+
updatedAt: daysAgo(30),
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "addr-admin-home-1707400018",
|
|
220
|
+
userId: "user-admin-letitrip",
|
|
221
|
+
label: "Home",
|
|
222
|
+
fullName: "LetItRip Admin",
|
|
223
|
+
phone: `${_phonePrefix}9876500000`,
|
|
224
|
+
addressLine1: "A-1204, Kalpataru Horizon",
|
|
225
|
+
addressLine2: "S.K. Ahire Marg, Worli",
|
|
226
|
+
landmark: "Near Worli Sea Face",
|
|
227
|
+
city: "Mumbai",
|
|
228
|
+
state: "Maharashtra",
|
|
229
|
+
postalCode: "400018",
|
|
230
|
+
country: _locale.countryName,
|
|
231
|
+
isDefault: false,
|
|
232
|
+
createdAt: daysAgo(300),
|
|
233
|
+
updatedAt: daysAgo(30),
|
|
217
234
|
},
|
|
218
235
|
];
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Carts Seed Data
|
|
2
|
+
* Carts Seed Data â€" LetItRip Collectibles
|
|
3
3
|
*
|
|
4
4
|
* Covers all cart states for testing Add-to-Cart / Update / Remove / Checkout flows:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* â€" Multi-item cart (cross-store, mixed categories) â€" Rahul Sharma
|
|
6
|
+
* â€" Single-item cart â€" Priya Patel
|
|
7
|
+
* â€" Cart with auction item â€" Arjun Singh
|
|
8
|
+
* â€" Cart with quantity > 1 â€" Meera Nair
|
|
9
|
+
* â€" Empty cart (items: []) â€" Amit Sharma
|
|
10
10
|
*
|
|
11
|
-
* Cart document ID = userId (O(1) lookup
|
|
11
|
+
* Cart document ID = userId (O(1) lookup â€" see cart.ts schema).
|
|
12
12
|
*
|
|
13
13
|
* All FK references:
|
|
14
|
-
* userId
|
|
15
|
-
* items[].productId
|
|
16
|
-
* items[].storeId
|
|
14
|
+
* userId â†' users/{uid} (see users-seed-data.ts)
|
|
15
|
+
* items[].productId â†' products/{id} (see products-standard/auctions-seed-data.ts)
|
|
16
|
+
* items[].storeId â†' stores/{id} (see stores-seed-data.ts)
|
|
17
17
|
*/
|
|
18
18
|
import type { CartDocument } from "../features/cart/schemas";
|
|
19
19
|
export declare const cartsSeedData: CartDocument[];
|