@mohasinac/appkit 3.2.20 → 3.3.0
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/checkout/actions.d.ts +2 -0
- package/dist/_internal/server/features/checkout/actions.js +239 -190
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +8 -11
- package/dist/_internal/server/jobs/core/emiInstallmentReminder.d.ts +8 -0
- package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +78 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +2 -2
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -10
- package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/providers/index.d.ts +4 -4
- package/dist/_internal/server/providers/index.js +4 -4
- package/dist/_internal/server/providers/payment/razorpay-mock.d.ts +3 -2
- package/dist/_internal/server/providers/payment/razorpay-mock.js +3 -1
- package/dist/_internal/server/providers/resolve.d.ts +10 -13
- package/dist/_internal/server/providers/resolve.js +9 -15
- package/dist/_internal/shared/actions/action-registry.js +28 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +1 -0
- package/dist/_internal/shared/checkout/rules/_registry.js +4 -0
- package/dist/_internal/shared/checkout/rules/art.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/art.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +1 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.js +1 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/index.js +2 -0
- package/dist/_internal/shared/checkout/rules/stickers.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/stickers.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +6 -0
- package/dist/_internal/shared/features/checkout/config.d.ts +16 -1
- package/dist/_internal/shared/features/checkout/config.js +16 -1
- package/dist/_internal/shared/features/emi/schedule.d.ts +63 -0
- package/dist/_internal/shared/features/emi/schedule.js +62 -0
- package/dist/_internal/shared/fees/calculator.d.ts +53 -0
- package/dist/_internal/shared/fees/calculator.js +40 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +22 -66
- package/dist/_internal/shared/listing-types/_registry.js +19 -2
- package/dist/_internal/shared/listing-types/art/config.d.ts +15 -0
- package/dist/_internal/shared/listing-types/art/config.js +14 -0
- package/dist/_internal/shared/listing-types/art/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/art/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/og.js +1 -0
- package/dist/_internal/shared/listing-types/art/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/art/schema.js +3 -0
- package/dist/_internal/shared/listing-types/art/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/auction/config.js +6 -0
- package/dist/_internal/shared/listing-types/capabilities.js +17 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/classified/config.js +6 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +6 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/live/config.js +6 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +6 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +6 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +8 -0
- package/dist/_internal/shared/listing-types/standard/config.js +6 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +15 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +14 -0
- package/dist/_internal/shared/listing-types/stickers/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/stickers/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/og.js +1 -0
- package/dist/_internal/shared/listing-types/stickers/schema.d.ts +2 -0
- package/dist/_internal/shared/listing-types/stickers/schema.js +3 -0
- package/dist/_internal/shared/listing-types/stickers/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/seed-factory.js +1 -0
- package/dist/client.d.ts +7 -2
- package/dist/client.js +4 -2
- package/dist/constants/field-names.d.ts +2 -6
- package/dist/constants/field-names.js +2 -6
- package/dist/contracts/index.d.ts +4 -2
- package/dist/contracts/index.js +6 -0
- package/dist/contracts/payment.d.ts +14 -9
- package/dist/contracts/payment.js +11 -1
- package/dist/contracts/shipping.d.ts +14 -9
- package/dist/contracts/shipping.js +11 -1
- package/dist/features/about/components/PublicProfileView.js +3 -6
- package/dist/features/admin/components/AdminAddressBookView.d.ts +11 -0
- package/dist/features/admin/components/AdminAddressBookView.js +30 -0
- package/dist/features/admin/components/AdminArtView.d.ts +3 -0
- package/dist/features/admin/components/AdminArtView.js +45 -0
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +14 -9
- package/dist/features/admin/components/AdminStickersView.d.ts +3 -0
- package/dist/features/admin/components/AdminStickersView.js +45 -0
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/schemas/firestore.d.ts +19 -11
- package/dist/features/admin/schemas/firestore.js +12 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +3 -2
- package/dist/features/auth/hooks/useAuth.js +35 -9
- package/dist/features/auth/schemas/firestore.d.ts +2 -6
- package/dist/features/cart/schemas/firestore.d.ts +3 -2
- package/dist/features/consultation/schemas/index.d.ts +2 -2
- package/dist/features/loyalty/schemas/index.d.ts +6 -6
- package/dist/features/orders/repository/orders.repository.d.ts +20 -2
- package/dist/features/orders/repository/orders.repository.js +34 -5
- package/dist/features/orders/schemas/firestore.d.ts +33 -7
- package/dist/features/orders/schemas/firestore.js +1 -1
- package/dist/features/orders/schemas/index.d.ts +176 -31
- package/dist/features/orders/schemas/index.js +20 -6
- package/dist/features/orders/types/index.d.ts +3 -3
- package/dist/features/payments/repository/payout.repository.js +2 -2
- package/dist/features/payments/schemas/firestore.d.ts +3 -3
- package/dist/features/payments/schemas/firestore.js +2 -2
- package/dist/features/payments/schemas/index.d.ts +5 -5
- package/dist/features/payments/schemas/index.js +2 -2
- package/dist/features/products/api/[id]/route.js +4 -1
- package/dist/features/products/api/route.js +4 -1
- package/dist/features/products/components/CompareOverlay.js +7 -9
- package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/components/ProductForm.js +18 -7
- package/dist/features/products/components/ProductGrid.js +7 -12
- package/dist/features/products/components/ShowGroupSection.js +3 -5
- package/dist/features/products/components/SublistingCarouselSection.js +3 -6
- package/dist/features/products/constants/listing-tabs.d.ts +32 -0
- package/dist/features/products/constants/listing-tabs.js +8 -0
- package/dist/features/products/index.d.ts +1 -1
- package/dist/features/products/index.js +3 -1
- package/dist/features/products/schemas/firestore.d.ts +20 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +48 -0
- package/dist/features/products/schemas/index.js +9 -0
- package/dist/features/products/schemas/product-features.d.ts +1 -1
- package/dist/features/products/types/index.d.ts +5 -3
- package/dist/features/products/utils/listing-type.d.ts +6 -0
- package/dist/features/products/utils/listing-type.js +3 -0
- package/dist/features/reviews/api/[id]/route.js +5 -4
- package/dist/features/seller/actions/seller-actions.d.ts +29 -14
- package/dist/features/seller/actions/seller-actions.js +101 -27
- package/dist/features/seller/components/SellerArtView.d.ts +7 -0
- package/dist/features/seller/components/SellerArtView.js +114 -0
- package/dist/features/seller/components/SellerOrdersView.js +38 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +5 -1
- package/dist/features/seller/components/SellerProductShell.js +69 -59
- package/dist/features/seller/components/SellerShippingView.js +6 -25
- package/dist/features/seller/components/SellerStickersView.d.ts +7 -0
- package/dist/features/seller/components/SellerStickersView.js +114 -0
- package/dist/features/seller/components/index.d.ts +4 -0
- package/dist/features/seller/components/index.js +2 -0
- package/dist/features/seller/messages/en.json +1 -1
- package/dist/features/seller/schemas/index.d.ts +7 -7
- package/dist/features/seller/schemas/index.js +1 -1
- package/dist/features/seller/types/index.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +18 -2
- package/dist/features/stores/schemas/firestore.d.ts +7 -1
- package/dist/features/stores/schemas/index.d.ts +19 -16
- package/dist/features/stores/schemas/index.js +1 -1
- package/dist/index.d.ts +21 -36
- package/dist/index.js +27 -39
- package/dist/next/routing/route-map.d.ts +22 -0
- package/dist/next/routing/route-map.js +10 -0
- package/dist/providers/payment-manual/index.d.ts +40 -0
- package/dist/providers/payment-manual/index.js +91 -0
- package/dist/providers/payment-razorpay/index.d.ts +4 -2
- package/dist/providers/payment-razorpay/index.js +4 -1
- package/dist/providers/shipping-manual/index.d.ts +22 -0
- package/dist/providers/shipping-manual/index.js +47 -0
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -3
- package/dist/schemas/registry.d.ts +129 -70
- package/dist/schemas/registry.js +0 -4
- package/dist/security/pii-encrypt.js +0 -7
- package/dist/security/pii-redact.js +0 -1
- package/dist/seed/conversations-seed-data.js +2 -2
- package/dist/seed/faq-seed-data.js +7 -7
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
- package/dist/seed/index.d.ts +2 -0
- package/dist/seed/index.js +2 -0
- package/dist/seed/manifest.js +20 -16
- package/dist/seed/payouts-seed-data.js +5 -5
- package/dist/seed/products-art-seed-data.d.ts +11 -0
- package/dist/seed/products-art-seed-data.js +125 -0
- package/dist/seed/products-preorders-seed-data.d.ts +2 -0
- package/dist/seed/products-stickers-seed-data.d.ts +11 -0
- package/dist/seed/products-stickers-seed-data.js +125 -0
- package/dist/seed/site-settings-seed-data.js +13 -4
- package/dist/seed/stores-seed-data.js +11 -11
- package/dist/server.d.ts +7 -15
- package/dist/server.js +17 -42
- package/package.json +1 -1
|
@@ -12,8 +12,23 @@ import { useMediaUpload } from "../../media";
|
|
|
12
12
|
import { resolveDate } from "../../../utils/date.formatter";
|
|
13
13
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
14
14
|
import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
|
|
15
|
-
import { isAuctionListing, isPreOrderListing, isPrizeDrawListing, } from "../utils/listing-type";
|
|
15
|
+
import { isAuctionListing, isPreOrderListing, isPrizeDrawListing, normalizeListingType, } from "../utils/listing-type";
|
|
16
16
|
import { PrizeDrawItemsEditor } from "./PrizeDrawItemsEditor";
|
|
17
|
+
// ListingType uses the canonical SB1-G spellings ("standard", "pre-order");
|
|
18
|
+
// ProductFeatureProductType predates it with legacy spellings for 2 values.
|
|
19
|
+
// Replaces the old 2-case ternary that silently mapped every other type
|
|
20
|
+
// (classified/digital-code/live/prize-draw/art/stickers) to "product".
|
|
21
|
+
const LISTING_TYPE_TO_FEATURE_PRODUCT_TYPE = {
|
|
22
|
+
standard: "product",
|
|
23
|
+
auction: "auction",
|
|
24
|
+
"pre-order": "preorder",
|
|
25
|
+
"prize-draw": "prize-draw",
|
|
26
|
+
classified: "classified",
|
|
27
|
+
"digital-code": "digital-code",
|
|
28
|
+
live: "live",
|
|
29
|
+
art: "art",
|
|
30
|
+
stickers: "stickers",
|
|
31
|
+
};
|
|
17
32
|
export const PRODUCT_STATUS_OPTIONS = [
|
|
18
33
|
{ value: "draft", label: "Draft" },
|
|
19
34
|
{ value: "published", label: "Published" },
|
|
@@ -122,7 +137,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
122
137
|
.split(",")
|
|
123
138
|
.map((tag) => tag.trim())
|
|
124
139
|
.filter(Boolean),
|
|
125
|
-
}), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly })] }), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
|
|
140
|
+
}), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowShipBeforeEmiComplete"), checked: !!product.allowShipBeforeEmiComplete, onChange: (e) => update({ allowShipBeforeEmiComplete: e.target.checked }), disabled: isReadonly })] }), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
|
|
126
141
|
condition: value,
|
|
127
142
|
}), disabled: isReadonly, options: [
|
|
128
143
|
{ value: "new", label: t("formConditionNew") },
|
|
@@ -199,11 +214,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
199
214
|
value: product.groupId,
|
|
200
215
|
onChange: (id) => update({ groupId: id || undefined }),
|
|
201
216
|
disabled: isReadonly,
|
|
202
|
-
}), !isAuctionListing(product) && renderGroupSettings?.(product), _jsx(ProductFeaturesSelector, { value: product.features ?? [], onChange: (features) => update({ features }), productType: (
|
|
203
|
-
? "auction"
|
|
204
|
-
: isPreOrderListing(product)
|
|
205
|
-
? "preorder"
|
|
206
|
-
: "product"), storeId: product.storeId, disabled: isReadonly }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Custom Sections" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Add up to 3 custom tabs to your product page \u2014 e.g. \"Box Contents\", \"Compatibility\", \"Grading Details\"." }), _jsx(CustomSectionsEditor, { sections: product.customSections ?? [], onChange: (sections) => update({ customSections: sections }) })] }), _jsx(FormField, { name: "shippingInfo", label: t("formShipping"), type: "textarea", value: product.shippingInfo || "", onChange: (value) => update({ shippingInfo: value }), disabled: isReadonly, placeholder: "Shipping information..." }), _jsx(FormField, { name: "returnPolicy", label: t("formReturnPolicy"), type: "textarea", value: product.returnPolicy || "", onChange: (value) => update({ returnPolicy: value }), disabled: isReadonly, placeholder: "Return policy details..." }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Physical Inventory" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Assign a storage location for warehouse or shelf organisation. Used by the Print & Label Center." }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { name: "physicalLocation.zone", label: "Zone", type: "text", placeholder: "e.g. A", value: product.physicalLocation?.zone ?? "", onChange: (v) => update({
|
|
217
|
+
}), !isAuctionListing(product) && renderGroupSettings?.(product), _jsx(ProductFeaturesSelector, { value: product.features ?? [], onChange: (features) => update({ features }), productType: LISTING_TYPE_TO_FEATURE_PRODUCT_TYPE[normalizeListingType(product)], storeId: product.storeId, disabled: isReadonly }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Custom Sections" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Add up to 3 custom tabs to your product page \u2014 e.g. \"Box Contents\", \"Compatibility\", \"Grading Details\"." }), _jsx(CustomSectionsEditor, { sections: product.customSections ?? [], onChange: (sections) => update({ customSections: sections }) })] }), _jsx(FormField, { name: "shippingInfo", label: t("formShipping"), type: "textarea", value: product.shippingInfo || "", onChange: (value) => update({ shippingInfo: value }), disabled: isReadonly, placeholder: "Shipping information..." }), _jsx(FormField, { name: "returnPolicy", label: t("formReturnPolicy"), type: "textarea", value: product.returnPolicy || "", onChange: (value) => update({ returnPolicy: value }), disabled: isReadonly, placeholder: "Return policy details..." }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Physical Inventory" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Assign a storage location for warehouse or shelf organisation. Used by the Print & Label Center." }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { name: "physicalLocation.zone", label: "Zone", type: "text", placeholder: "e.g. A", value: product.physicalLocation?.zone ?? "", onChange: (v) => update({
|
|
207
218
|
physicalLocation: {
|
|
208
219
|
zone: v,
|
|
209
220
|
shelf: product.physicalLocation?.shelf ?? "",
|
|
@@ -11,15 +11,14 @@ import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
|
11
11
|
import { FeatureBadgeList } from "./FeatureBadge";
|
|
12
12
|
import { useProductFeatures } from "./ProductFeaturesContext";
|
|
13
13
|
import { PRODUCT_FEATURE_CARD_MAX_VISIBLE } from "../constants/product-features.constants";
|
|
14
|
-
import {
|
|
14
|
+
import { normalizeListingType } from "../utils/listing-type";
|
|
15
|
+
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
15
16
|
const __P = {
|
|
16
17
|
p3: "p-3",
|
|
17
18
|
};
|
|
18
19
|
const __O = {
|
|
19
20
|
hidden: "overflow-hidden",
|
|
20
21
|
};
|
|
21
|
-
const CLS_BADGE_AUCTION = "bg-warning-surface text-white";
|
|
22
|
-
const CLS_BADGE_PREORDER = "bg-violet-600 text-white";
|
|
23
22
|
const CLS_BADGE_NEW = "rounded-full bg-error-surface px-2 py-0.5 text-[10px] text-white shadow-sm";
|
|
24
23
|
const CLS_BADGE_SALE = "rounded-full bg-success-surface px-2 py-0.5 text-[10px] font-bold text-white shadow-sm";
|
|
25
24
|
const CLS_BADGE_TRENDING = "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm";
|
|
@@ -38,14 +37,10 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
38
37
|
? Math.round(((product.originalPrice - product.price) / product.originalPrice) *
|
|
39
38
|
100)
|
|
40
39
|
: null;
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
? { label: "Auction", cls: CLS_BADGE_AUCTION }
|
|
46
|
-
: isPreOrder
|
|
47
|
-
? { label: "Pre-Order", cls: CLS_BADGE_PREORDER }
|
|
48
|
-
: null;
|
|
40
|
+
const listingPlugin = pluginFor(normalizeListingType(product));
|
|
41
|
+
const typeBadge = listingPlugin.badge
|
|
42
|
+
? { label: listingPlugin.badge.label, cls: listingPlugin.badge.className }
|
|
43
|
+
: null;
|
|
49
44
|
const longPress = useLongPress(() => onSelect?.(product.id));
|
|
50
45
|
const featuresList = useProductFeatures();
|
|
51
46
|
const handleCardClick = selectionMode
|
|
@@ -97,7 +92,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
97
92
|
return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px]", color: "faint", children: ["by ", seller] })) : null;
|
|
98
93
|
})(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1", gap: "xs", children: [_jsx(Span, { className: CLS_STAR, children: "\u2605" }), _jsxs(Span, { className: "text-[11px]", color: "muted", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto", padding: "t-xs", children: [_jsxs(Row, { align: "baseline", gap: "sm", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { size: "xs", className: "line-through", color: "faint", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
|
|
99
94
|
const stock = product.stockCount ?? product.stockQuantity ?? product.availableQuantity;
|
|
100
|
-
if (stock === undefined ||
|
|
95
|
+
if (stock === undefined || !listingPlugin.showsStockQuantity)
|
|
101
96
|
return null;
|
|
102
97
|
if (stock <= 0) {
|
|
103
98
|
return (_jsx(Text, { className: CLS_DISCOUNT_TEXT, children: "Out of stock" }));
|
|
@@ -2,20 +2,18 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
|
-
import { ROUTES } from "../../../next";
|
|
6
5
|
import { Div, Row, Span, Table, Thead, Tbody, Tr, Th, Td, Text, Modal, SideDrawer, Button } from "../../../ui";
|
|
7
6
|
import { MediaImage } from "../../media/MediaImage";
|
|
8
7
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
9
|
-
import {
|
|
8
|
+
import { normalizeListingType } from "../utils/listing-type";
|
|
9
|
+
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
10
10
|
const __O = {
|
|
11
11
|
hidden: "overflow-hidden",
|
|
12
12
|
xAuto: "overflow-x-auto",
|
|
13
13
|
};
|
|
14
14
|
function memberHref(m) {
|
|
15
15
|
const slug = m.slug ?? m.id;
|
|
16
|
-
|
|
17
|
-
return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug));
|
|
18
|
-
return String(ROUTES.PUBLIC.PRODUCT_DETAIL(slug));
|
|
16
|
+
return pluginFor(normalizeListingType(m)).detailRoute(slug);
|
|
19
17
|
}
|
|
20
18
|
function MemberThumb({ member, isCurrent }) {
|
|
21
19
|
const image = member.images?.[0] ?? "";
|
|
@@ -5,18 +5,15 @@ import Link from "next/link";
|
|
|
5
5
|
import { ROUTES } from "../../../next";
|
|
6
6
|
import { Div, Row, Span, Text } from "../../../ui";
|
|
7
7
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
8
|
-
import {
|
|
8
|
+
import { normalizeListingType } from "../utils/listing-type";
|
|
9
|
+
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
9
10
|
const __O = {
|
|
10
11
|
hidden: "overflow-hidden",
|
|
11
12
|
xAuto: "overflow-x-auto",
|
|
12
13
|
};
|
|
13
14
|
function getHref(listing) {
|
|
14
15
|
const slug = listing.slug ?? listing.id;
|
|
15
|
-
|
|
16
|
-
return String(ROUTES.PUBLIC.AUCTION_DETAIL(slug));
|
|
17
|
-
if (isPreOrderListing(listing))
|
|
18
|
-
return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug));
|
|
19
|
-
return String(ROUTES.PUBLIC.PRODUCT_DETAIL(slug));
|
|
16
|
+
return pluginFor(normalizeListingType(listing)).detailRoute(slug);
|
|
20
17
|
}
|
|
21
18
|
function ListingThumb({ listing, isCurrent, }) {
|
|
22
19
|
const image = listing.images?.[0] ?? listing.mainImage ?? "";
|
|
@@ -52,6 +52,14 @@ export declare const CATEGORY_PAGE_TABS: readonly [{
|
|
|
52
52
|
readonly id: "live";
|
|
53
53
|
readonly label: "Live Items";
|
|
54
54
|
readonly listingType: "live";
|
|
55
|
+
}, {
|
|
56
|
+
readonly id: "art";
|
|
57
|
+
readonly label: "Art";
|
|
58
|
+
readonly listingType: "art";
|
|
59
|
+
}, {
|
|
60
|
+
readonly id: "stickers";
|
|
61
|
+
readonly label: "Stickers";
|
|
62
|
+
readonly listingType: "stickers";
|
|
55
63
|
}, {
|
|
56
64
|
readonly id: "stores";
|
|
57
65
|
readonly label: "Stores";
|
|
@@ -91,6 +99,14 @@ export declare const STORE_PAGE_TABS: readonly [{
|
|
|
91
99
|
readonly id: "live";
|
|
92
100
|
readonly label: "Live Items";
|
|
93
101
|
readonly listingType: "live";
|
|
102
|
+
}, {
|
|
103
|
+
readonly id: "art";
|
|
104
|
+
readonly label: "Art";
|
|
105
|
+
readonly listingType: "art";
|
|
106
|
+
}, {
|
|
107
|
+
readonly id: "stickers";
|
|
108
|
+
readonly label: "Stickers";
|
|
109
|
+
readonly listingType: "stickers";
|
|
94
110
|
}];
|
|
95
111
|
export type StoreTabId = (typeof STORE_PAGE_TABS)[number]["id"];
|
|
96
112
|
/** Tabs shown on the seller-dashboard listings view + admin products list. */
|
|
@@ -125,6 +141,14 @@ export declare const SELLER_LISTING_TABS: readonly [{
|
|
|
125
141
|
readonly id: "live";
|
|
126
142
|
readonly label: "Live Items";
|
|
127
143
|
readonly listingType: "live";
|
|
144
|
+
}, {
|
|
145
|
+
readonly id: "art";
|
|
146
|
+
readonly label: "Art";
|
|
147
|
+
readonly listingType: "art";
|
|
148
|
+
}, {
|
|
149
|
+
readonly id: "stickers";
|
|
150
|
+
readonly label: "Stickers";
|
|
151
|
+
readonly listingType: "stickers";
|
|
128
152
|
}];
|
|
129
153
|
export type SellerListingTabId = (typeof SELLER_LISTING_TABS)[number]["id"];
|
|
130
154
|
/** Tabs shown on `/search` results. */
|
|
@@ -163,5 +187,13 @@ export declare const SEARCH_RESULT_TABS: readonly [{
|
|
|
163
187
|
readonly id: "live";
|
|
164
188
|
readonly label: "Live Items";
|
|
165
189
|
readonly listingType: "live";
|
|
190
|
+
}, {
|
|
191
|
+
readonly id: "art";
|
|
192
|
+
readonly label: "Art";
|
|
193
|
+
readonly listingType: "art";
|
|
194
|
+
}, {
|
|
195
|
+
readonly id: "stickers";
|
|
196
|
+
readonly label: "Stickers";
|
|
197
|
+
readonly listingType: "stickers";
|
|
166
198
|
}];
|
|
167
199
|
export type SearchTabId = (typeof SEARCH_RESULT_TABS)[number]["id"];
|
|
@@ -18,6 +18,8 @@ export const CATEGORY_PAGE_TABS = [
|
|
|
18
18
|
{ id: "classifieds", label: "Classifieds", listingType: "classified" },
|
|
19
19
|
{ id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
|
|
20
20
|
{ id: "live", label: "Live Items", listingType: "live" },
|
|
21
|
+
{ id: "art", label: "Art", listingType: "art" },
|
|
22
|
+
{ id: "stickers", label: "Stickers", listingType: "stickers" },
|
|
21
23
|
{ id: "stores", label: "Stores", entity: "stores" },
|
|
22
24
|
];
|
|
23
25
|
/** Tabs shown on the public `/stores/[slug]` nav bar. */
|
|
@@ -30,6 +32,8 @@ export const STORE_PAGE_TABS = [
|
|
|
30
32
|
{ id: "classifieds", label: "Classifieds", listingType: "classified" },
|
|
31
33
|
{ id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
|
|
32
34
|
{ id: "live", label: "Live Items", listingType: "live" },
|
|
35
|
+
{ id: "art", label: "Art", listingType: "art" },
|
|
36
|
+
{ id: "stickers", label: "Stickers", listingType: "stickers" },
|
|
33
37
|
];
|
|
34
38
|
/** Tabs shown on the seller-dashboard listings view + admin products list. */
|
|
35
39
|
export const SELLER_LISTING_TABS = [
|
|
@@ -41,6 +45,8 @@ export const SELLER_LISTING_TABS = [
|
|
|
41
45
|
{ id: "classifieds", label: "Classifieds", listingType: "classified" },
|
|
42
46
|
{ id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
|
|
43
47
|
{ id: "live", label: "Live Items", listingType: "live" },
|
|
48
|
+
{ id: "art", label: "Art", listingType: "art" },
|
|
49
|
+
{ id: "stickers", label: "Stickers", listingType: "stickers" },
|
|
44
50
|
];
|
|
45
51
|
/** Tabs shown on `/search` results. */
|
|
46
52
|
export const SEARCH_RESULT_TABS = [
|
|
@@ -53,4 +59,6 @@ export const SEARCH_RESULT_TABS = [
|
|
|
53
59
|
{ id: "classifieds", label: "Classifieds", listingType: "classified" },
|
|
54
60
|
{ id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
|
|
55
61
|
{ id: "live", label: "Live Items", listingType: "live" },
|
|
62
|
+
{ id: "art", label: "Art", listingType: "art" },
|
|
63
|
+
{ id: "stickers", label: "Stickers", listingType: "stickers" },
|
|
56
64
|
];
|
|
@@ -9,5 +9,5 @@ export * from "./components";
|
|
|
9
9
|
export * from "./schemas";
|
|
10
10
|
export * from "./columns";
|
|
11
11
|
export { manifest } from "./manifest";
|
|
12
|
-
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, } from "./utils/listing-type";
|
|
12
|
+
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./utils/listing-type";
|
|
13
13
|
export { sanitizeProductForPublic, sanitizeProductsForPublic, } from "./utils/sanitize";
|
|
@@ -11,5 +11,7 @@ export * from "./columns";
|
|
|
11
11
|
export { manifest } from "./manifest";
|
|
12
12
|
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing,
|
|
13
13
|
// SB-UNI-F 2026-05-13 — Phase 2 predicates.
|
|
14
|
-
isClassifiedListing, isDigitalCodeListing, isLiveListing,
|
|
14
|
+
isClassifiedListing, isDigitalCodeListing, isLiveListing,
|
|
15
|
+
// Art/stickers session.
|
|
16
|
+
isArtListing, isStickersListing, } from "./utils/listing-type";
|
|
15
17
|
export { sanitizeProductForPublic, sanitizeProductsForPublic, } from "./utils/sanitize";
|
|
@@ -105,6 +105,21 @@ export interface ProductLiveItemMeta {
|
|
|
105
105
|
/** CITES permit number (Appendix I/II species). */
|
|
106
106
|
cites?: string;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Print-specific metadata shared by the "art" and "stickers" listing types
|
|
110
|
+
* (both are printed-only physical goods). Every field optional so partial
|
|
111
|
+
* seller input still saves.
|
|
112
|
+
*/
|
|
113
|
+
export interface ProductPrintMeta {
|
|
114
|
+
/** e.g. "A4", "12x18 in", "5x5 cm sheet". */
|
|
115
|
+
size?: string;
|
|
116
|
+
/** e.g. "Matte photo paper", "Vinyl", "Canvas". */
|
|
117
|
+
material?: string;
|
|
118
|
+
/** e.g. "Glossy", "Matte", "Holographic". */
|
|
119
|
+
finish?: string;
|
|
120
|
+
/** Limited-edition print run size; omitted for open editions. */
|
|
121
|
+
editionSize?: number;
|
|
122
|
+
}
|
|
108
123
|
export interface ProductDocument extends BaseDocument {
|
|
109
124
|
title: string;
|
|
110
125
|
description: string;
|
|
@@ -178,6 +193,10 @@ export interface ProductDocument extends BaseDocument {
|
|
|
178
193
|
isPromoted?: boolean;
|
|
179
194
|
isOnSale?: boolean;
|
|
180
195
|
isSold?: boolean;
|
|
196
|
+
/** When true, an EMI order for this product ships as soon as it's confirmed instead of waiting for every installment to be paid. Default false. */
|
|
197
|
+
allowShipBeforeEmiComplete?: boolean;
|
|
198
|
+
/** Print-specific metadata for "art" / "stickers" listings — printed-only physical goods. Optional on every listing type; only populated for art/stickers. */
|
|
199
|
+
printMeta?: ProductPrintMeta;
|
|
181
200
|
promotionEndDate?: Date;
|
|
182
201
|
pickupAddressId?: string;
|
|
183
202
|
viewCount?: number;
|
|
@@ -313,7 +332,7 @@ export declare const PRODUCT_COLLECTION: "products";
|
|
|
313
332
|
export declare const PRODUCT_INDEXED_FIELDS: readonly ["storeId", "status", "category", "featured", "listingType", "isPromoted", "isOnSale", "isSold", "searchTokens", "createdAt", "barcodeId"];
|
|
314
333
|
export declare const DEFAULT_PRODUCT_DATA: Partial<ProductDocument>;
|
|
315
334
|
export declare const PRODUCT_PUBLIC_FIELDS: readonly ["id", "title", "description", "category", "subcategory", "brand", "price", "currency", "stockQuantity", "availableQuantity", "images", "status", "storeName", "featured", "tags", "specifications", "features", "shippingInfo", "returnPolicy", "listingType", "auctionEndDate", "startingBid", "currentBid", "bidCount", "reservePrice", "buyNowPrice", "minBidIncrement", "autoExtendable", "auctionExtensionMinutes", "auctionShippingPaidBy", "buyItNowPriceInPaise", "bidsHaveStarted", "grading", "card", "classified", "digitalCode", "liveItem", "catalogProductId", "preOrderDeliveryDate", "preOrderDepositPercent", "preOrderDepositAmount", "preOrderMaxQuantity", "preOrderCurrentCount", "preOrderProductionStatus", "preOrderCancellable", "condition", "insurance", "insuranceCost", "shippingPaidBy", "isPromoted", "isOnSale", "isSold", "slug", "seoTitle", "seoDescription", "seoKeywords", "viewCount", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle", "createdAt"];
|
|
316
|
-
export declare const PRODUCT_UPDATABLE_FIELDS: readonly ["title", "description", "category", "subcategory", "brand", "price", "stockQuantity", "images", "status", "tags", "specifications", "features", "shippingInfo", "returnPolicy", "pickupAddressId", "condition", "insurance", "shippingPaidBy", "autoExtendable", "auctionExtensionMinutes", "auctionShippingPaidBy", "reservePrice", "buyNowPrice", "minBidIncrement", "buyItNowPriceInPaise", "bidsHaveStarted", "grading", "card", "classified", "digitalCode", "liveItem", "catalogProductId", "listingType", "preOrderDeliveryDate", "preOrderDepositPercent", "preOrderDepositAmount", "preOrderMaxQuantity", "preOrderProductionStatus", "preOrderCancellable", "isOnSale", "isSold", "seoTitle", "seoDescription", "seoKeywords", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle", "barcodeId"];
|
|
335
|
+
export declare const PRODUCT_UPDATABLE_FIELDS: readonly ["title", "description", "category", "subcategory", "brand", "price", "stockQuantity", "images", "status", "tags", "specifications", "features", "shippingInfo", "returnPolicy", "pickupAddressId", "condition", "insurance", "shippingPaidBy", "autoExtendable", "auctionExtensionMinutes", "auctionShippingPaidBy", "reservePrice", "buyNowPrice", "minBidIncrement", "buyItNowPriceInPaise", "bidsHaveStarted", "grading", "card", "classified", "digitalCode", "liveItem", "catalogProductId", "listingType", "preOrderDeliveryDate", "preOrderDepositPercent", "preOrderDepositAmount", "preOrderMaxQuantity", "preOrderProductionStatus", "preOrderCancellable", "isOnSale", "isSold", "allowShipBeforeEmiComplete", "printMeta", "seoTitle", "seoDescription", "seoKeywords", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle", "barcodeId"];
|
|
317
336
|
export type ProductCreateInput = Omit<ProductDocument, "id" | "createdAt" | "updatedAt" | "availableQuantity" | "bidCount" | "currentBid" | "auctionOriginalEndDate">;
|
|
318
337
|
export type ProductUpdateInput = Partial<Pick<ProductDocument, (typeof PRODUCT_UPDATABLE_FIELDS)[number]>>;
|
|
319
338
|
export type ProductAdminUpdateInput = Partial<Omit<ProductDocument, "id" | "createdAt">>;
|
|
@@ -29,6 +29,23 @@ export declare const productSeoSchema: z.ZodObject<{
|
|
|
29
29
|
description?: string | undefined;
|
|
30
30
|
keywords?: string[] | undefined;
|
|
31
31
|
}>;
|
|
32
|
+
/** Print-specific metadata for "art" / "stickers" listings. */
|
|
33
|
+
export declare const printMetaSchema: z.ZodObject<{
|
|
34
|
+
size: z.ZodOptional<z.ZodString>;
|
|
35
|
+
material: z.ZodOptional<z.ZodString>;
|
|
36
|
+
finish: z.ZodOptional<z.ZodString>;
|
|
37
|
+
editionSize: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
size?: string | undefined;
|
|
40
|
+
material?: string | undefined;
|
|
41
|
+
finish?: string | undefined;
|
|
42
|
+
editionSize?: number | undefined;
|
|
43
|
+
}, {
|
|
44
|
+
size?: string | undefined;
|
|
45
|
+
material?: string | undefined;
|
|
46
|
+
finish?: string | undefined;
|
|
47
|
+
editionSize?: number | undefined;
|
|
48
|
+
}>;
|
|
32
49
|
/**
|
|
33
50
|
* Base Zod schema for a product item.
|
|
34
51
|
* Apps can extend this to add their own fields:
|
|
@@ -86,6 +103,23 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
86
103
|
}>>;
|
|
87
104
|
featured: z.ZodOptional<z.ZodBoolean>;
|
|
88
105
|
isPromoted: z.ZodOptional<z.ZodBoolean>;
|
|
106
|
+
allowShipBeforeEmiComplete: z.ZodOptional<z.ZodBoolean>;
|
|
107
|
+
printMeta: z.ZodOptional<z.ZodObject<{
|
|
108
|
+
size: z.ZodOptional<z.ZodString>;
|
|
109
|
+
material: z.ZodOptional<z.ZodString>;
|
|
110
|
+
finish: z.ZodOptional<z.ZodString>;
|
|
111
|
+
editionSize: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
}, "strip", z.ZodTypeAny, {
|
|
113
|
+
size?: string | undefined;
|
|
114
|
+
material?: string | undefined;
|
|
115
|
+
finish?: string | undefined;
|
|
116
|
+
editionSize?: number | undefined;
|
|
117
|
+
}, {
|
|
118
|
+
size?: string | undefined;
|
|
119
|
+
material?: string | undefined;
|
|
120
|
+
finish?: string | undefined;
|
|
121
|
+
editionSize?: number | undefined;
|
|
122
|
+
}>>;
|
|
89
123
|
currentBid: z.ZodOptional<z.ZodNumber>;
|
|
90
124
|
availableQuantity: z.ZodOptional<z.ZodNumber>;
|
|
91
125
|
category: z.ZodOptional<z.ZodString>;
|
|
@@ -337,6 +371,13 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
337
371
|
preOrderCurrentCount?: number | undefined;
|
|
338
372
|
preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
|
|
339
373
|
preOrderCancellable?: boolean | undefined;
|
|
374
|
+
allowShipBeforeEmiComplete?: boolean | undefined;
|
|
375
|
+
printMeta?: {
|
|
376
|
+
size?: string | undefined;
|
|
377
|
+
material?: string | undefined;
|
|
378
|
+
finish?: string | undefined;
|
|
379
|
+
editionSize?: number | undefined;
|
|
380
|
+
} | undefined;
|
|
340
381
|
pickupAddressId?: string | undefined;
|
|
341
382
|
viewCount?: number | undefined;
|
|
342
383
|
avgRating?: number | undefined;
|
|
@@ -466,6 +507,13 @@ export declare const productItemSchema: z.ZodObject<{
|
|
|
466
507
|
preOrderCurrentCount?: number | undefined;
|
|
467
508
|
preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
|
|
468
509
|
preOrderCancellable?: boolean | undefined;
|
|
510
|
+
allowShipBeforeEmiComplete?: boolean | undefined;
|
|
511
|
+
printMeta?: {
|
|
512
|
+
size?: string | undefined;
|
|
513
|
+
material?: string | undefined;
|
|
514
|
+
finish?: string | undefined;
|
|
515
|
+
editionSize?: number | undefined;
|
|
516
|
+
} | undefined;
|
|
469
517
|
pickupAddressId?: string | undefined;
|
|
470
518
|
viewCount?: number | undefined;
|
|
471
519
|
avgRating?: number | undefined;
|
|
@@ -13,6 +13,13 @@ export const productSeoSchema = z.object({
|
|
|
13
13
|
description: z.string().optional(),
|
|
14
14
|
keywords: z.array(z.string()).optional(),
|
|
15
15
|
});
|
|
16
|
+
/** Print-specific metadata for "art" / "stickers" listings. */
|
|
17
|
+
export const printMetaSchema = z.object({
|
|
18
|
+
size: z.string().max(80).optional(),
|
|
19
|
+
material: z.string().max(80).optional(),
|
|
20
|
+
finish: z.string().max(80).optional(),
|
|
21
|
+
editionSize: z.number().int().positive().optional(),
|
|
22
|
+
});
|
|
16
23
|
// --- Base item schema ---------------------------------------------------------
|
|
17
24
|
/**
|
|
18
25
|
* Base Zod schema for a product item.
|
|
@@ -43,6 +50,8 @@ export const productItemSchema = z.object({
|
|
|
43
50
|
.optional(),
|
|
44
51
|
featured: z.boolean().optional(),
|
|
45
52
|
isPromoted: z.boolean().optional(),
|
|
53
|
+
allowShipBeforeEmiComplete: z.boolean().optional(),
|
|
54
|
+
printMeta: printMetaSchema.optional(),
|
|
46
55
|
currentBid: z.number().optional(),
|
|
47
56
|
availableQuantity: z.number().optional(),
|
|
48
57
|
category: z.string().optional(),
|
|
@@ -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" | "prize-draw" | "classified" | "digital-code" | "live" | "all";
|
|
23
|
+
export type ProductFeatureProductType = "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers" | "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().
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MediaField } from "../../media/types/index";
|
|
2
|
-
import type { CustomField, CustomSection } from "../schemas/firestore";
|
|
3
|
-
export type { CustomField, CustomSection } from "../schemas/firestore";
|
|
2
|
+
import type { CustomField, CustomSection, ProductPrintMeta } from "../schemas/firestore";
|
|
3
|
+
export type { CustomField, CustomSection, ProductPrintMeta } from "../schemas/firestore";
|
|
4
4
|
export type ProductStatus = "draft" | "published" | "in_review" | "archived";
|
|
5
5
|
export type ProductCondition = "new" | "like_new" | "good" | "fair" | "poor" | "used" | "refurbished" | "broken" | "graded";
|
|
6
6
|
/**
|
|
@@ -10,7 +10,7 @@ export type ProductCondition = "new" | "like_new" | "good" | "fair" | "poor" | "
|
|
|
10
10
|
* accepts legacy alias tokens (`preorder` → `pre-order`, `product` → `standard`)
|
|
11
11
|
* but stored values are always one of these five.
|
|
12
12
|
*/
|
|
13
|
-
export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
|
|
13
|
+
export type ListingType = "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live" | "art" | "stickers";
|
|
14
14
|
export interface ProductImage {
|
|
15
15
|
url: string;
|
|
16
16
|
thumbnailUrl?: string;
|
|
@@ -39,6 +39,8 @@ export interface ProductItem {
|
|
|
39
39
|
};
|
|
40
40
|
featured?: boolean;
|
|
41
41
|
isPromoted?: boolean;
|
|
42
|
+
allowShipBeforeEmiComplete?: boolean;
|
|
43
|
+
printMeta?: ProductPrintMeta;
|
|
42
44
|
currentBid?: number;
|
|
43
45
|
availableQuantity?: number;
|
|
44
46
|
/** @deprecated Use categorySlugs[0] */
|
|
@@ -33,3 +33,9 @@ export declare const isDigitalCodeListing: (input?: {
|
|
|
33
33
|
export declare const isLiveListing: (input?: {
|
|
34
34
|
listingType?: ListingType;
|
|
35
35
|
}) => boolean;
|
|
36
|
+
export declare const isArtListing: (input?: {
|
|
37
|
+
listingType?: ListingType;
|
|
38
|
+
}) => boolean;
|
|
39
|
+
export declare const isStickersListing: (input?: {
|
|
40
|
+
listingType?: ListingType;
|
|
41
|
+
}) => boolean;
|
|
@@ -19,6 +19,9 @@ export const isPrizeDrawListing = (input) => normalizeListingType(input) === "pr
|
|
|
19
19
|
export const isClassifiedListing = (input) => normalizeListingType(input) === "classified";
|
|
20
20
|
export const isDigitalCodeListing = (input) => normalizeListingType(input) === "digital-code";
|
|
21
21
|
export const isLiveListing = (input) => normalizeListingType(input) === "live";
|
|
22
|
+
// Art/stickers session — printed-only physical-goods listing types.
|
|
23
|
+
export const isArtListing = (input) => normalizeListingType(input) === "art";
|
|
24
|
+
export const isStickersListing = (input) => normalizeListingType(input) === "stickers";
|
|
22
25
|
// SB-UNI-D — isBundleListing removed. Bundles are now a categoryType
|
|
23
26
|
// discriminator on CategoryDocument; consumers should query
|
|
24
27
|
// `categoriesRepository.findBySlugAndType(slug, "bundle")` instead.
|
|
@@ -13,6 +13,7 @@ import { z } from "zod";
|
|
|
13
13
|
import { getProviders } from "../../../../contracts";
|
|
14
14
|
import { createRouteHandler } from "../../../../next";
|
|
15
15
|
import { normalizeError } from "../../../../errors/normalize";
|
|
16
|
+
import { isAdminUser, isModeratorUser } from "../../../auth/role-predicates";
|
|
16
17
|
const ERR_REVIEW_NOT_FOUND = "Review not found";
|
|
17
18
|
// ---- GET /api/reviews/[id] ---------------------------------------------------
|
|
18
19
|
export async function GET(_request, context) {
|
|
@@ -70,8 +71,8 @@ export const reviewItemPATCH = createRouteHandler({
|
|
|
70
71
|
if (!review)
|
|
71
72
|
return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
|
|
72
73
|
const isOwner = review.userId === user.uid;
|
|
73
|
-
const isModerator = user
|
|
74
|
-
const isAdmin = user
|
|
74
|
+
const isModerator = isModeratorUser(user);
|
|
75
|
+
const isAdmin = isAdminUser(user);
|
|
75
76
|
if (!isOwner && !isModerator && !isAdmin) {
|
|
76
77
|
return NextResponse.json({ success: false, error: "Forbidden" }, { status: 403 });
|
|
77
78
|
}
|
|
@@ -101,8 +102,8 @@ export const reviewItemDELETE = createRouteHandler({
|
|
|
101
102
|
if (!review)
|
|
102
103
|
return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
|
|
103
104
|
const isOwner = review.userId === user.uid;
|
|
104
|
-
const isModerator = user
|
|
105
|
-
const isAdmin = user
|
|
105
|
+
const isModerator = isModeratorUser(user);
|
|
106
|
+
const isAdmin = isAdminUser(user);
|
|
106
107
|
if (!isOwner && !isModerator && !isAdmin) {
|
|
107
108
|
return NextResponse.json({ success: false, error: "Forbidden" }, { status: 403 });
|
|
108
109
|
}
|
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
* explicit params. Called by letitrip thin wrappers that handle auth,
|
|
6
6
|
* rate-limiting, and user-facing validation.
|
|
7
7
|
*
|
|
8
|
-
* Note:
|
|
9
|
-
*
|
|
10
|
-
* thin wrapper because they use the @/lib/shiprocket/client SDK which is
|
|
11
|
-
* a permanent letitrip-only dependency.
|
|
8
|
+
* Note: updateSellerShipping remains in the letitrip thin wrapper
|
|
9
|
+
* (src/actions/seller.actions.ts) alongside the other seller server actions.
|
|
12
10
|
*/
|
|
13
11
|
import type { JsonValue } from "../../../schemas/types";
|
|
14
|
-
import type { SellerPayoutDetails } from "../../auth/schemas/firestore";
|
|
12
|
+
import type { SellerPayoutDetails, SellerShippingConfig } from "../../auth/schemas/firestore";
|
|
15
13
|
import type { StoreDocument } from "../../stores/schemas/firestore";
|
|
16
14
|
import type { ProductDocument } from "../../products/schemas/firestore";
|
|
17
15
|
import type { OrderDocument } from "../../orders/schemas/firestore";
|
|
@@ -86,6 +84,14 @@ export interface CustomShipOrderInput {
|
|
|
86
84
|
trackingNumber: string;
|
|
87
85
|
trackingUrl: string;
|
|
88
86
|
}
|
|
87
|
+
export interface MarkEmiInstallmentPaidInput {
|
|
88
|
+
/** 1-based installment number, matches EmiInstallment.index. */
|
|
89
|
+
installmentIndex: number;
|
|
90
|
+
/** Buyer-supplied UTR/reference number for this installment's manual payment. */
|
|
91
|
+
transactionId?: string;
|
|
92
|
+
/** Signed-URL media slug for the buyer's payment proof for this installment. */
|
|
93
|
+
proofUrl?: string;
|
|
94
|
+
}
|
|
89
95
|
export interface SellerListParams {
|
|
90
96
|
filters?: string;
|
|
91
97
|
sorts?: string;
|
|
@@ -109,15 +115,7 @@ export declare function requestPayout(userId: string, userName: string, userEmai
|
|
|
109
115
|
export declare function bulkSellerOrder(userId: string, userRole: string, userDisplayName: string, userEmail: string, orderIds: string[]): Promise<BulkSellerOrderResult>;
|
|
110
116
|
export declare function createSellerProduct(userId: string, userName: string, userEmail: string, input: Record<string, JsonValue>): Promise<void>;
|
|
111
117
|
export declare function getSellerStore(userId: string): Promise<StoreDocument | null>;
|
|
112
|
-
export declare function getSellerShipping(userId: string): Promise<
|
|
113
|
-
method: import("../../auth").SellerShippingMethod;
|
|
114
|
-
customShippingPrice?: number;
|
|
115
|
-
customCarrierName?: string;
|
|
116
|
-
shiprocketEmail?: string;
|
|
117
|
-
shiprocketTokenExpiry?: Date;
|
|
118
|
-
pickupAddress?: import("../../auth").SellerPickupAddress;
|
|
119
|
-
isConfigured: boolean;
|
|
120
|
-
} | null>;
|
|
118
|
+
export declare function getSellerShipping(userId: string): Promise<SellerShippingConfig | null>;
|
|
121
119
|
export declare function getSellerPayoutSettings(userId: string): Promise<SellerPayoutDetails | {
|
|
122
120
|
method: string;
|
|
123
121
|
isConfigured: boolean;
|
|
@@ -166,8 +164,25 @@ export declare function listSellerMyProducts(userId: string, params?: SellerList
|
|
|
166
164
|
}>;
|
|
167
165
|
export declare function sellerUpdateProduct(userId: string, userRole: string, productId: string, input: Record<string, JsonValue>): Promise<ProductDocument>;
|
|
168
166
|
export declare function sellerDeleteProduct(userId: string, userRole: string, productId: string): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* EMI orders stay unshipped until every installment is paid, UNLESS every
|
|
169
|
+
* product in the order has its `allowShipBeforeEmiComplete` flag set. This
|
|
170
|
+
* is the single choke point for the "ship" transition, so it can't be
|
|
171
|
+
* bypassed by a different route/action reaching `orderRepository.update`
|
|
172
|
+
* directly with `status: "shipped"`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function assertEmiShippable(order: OrderDocument): Promise<void>;
|
|
169
175
|
export declare function customShipOrder(userId: string, userRole: string, orderId: string, input: CustomShipOrderInput): Promise<{
|
|
170
176
|
orderId: string;
|
|
171
177
|
method: string;
|
|
172
178
|
}>;
|
|
179
|
+
/**
|
|
180
|
+
* Records collection of one EMI installment. Seller/admin marks the
|
|
181
|
+
* installment paid after verifying the buyer's manual transfer (UTR +
|
|
182
|
+
* proof). Recomputes `emiRemainingBalance` from the still-pending
|
|
183
|
+
* installments and flips `emiComplete` once none remain — that flag is the
|
|
184
|
+
* single gate `assertEmiShippable` reads, so this is the only place an
|
|
185
|
+
* EMI order can become shippable.
|
|
186
|
+
*/
|
|
187
|
+
export declare function markEmiInstallmentPaid(userId: string, userRole: string, orderId: string, input: MarkEmiInstallmentPaidInput): Promise<OrderDocument>;
|
|
173
188
|
export {};
|