@mohasinac/appkit 4.41.1 → 4.41.3
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/features/admin/components/AdminCarouselView.js +18 -2
- package/dist/features/admin/components/AdminCategoriesView.js +20 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/seller/schemas/coupon-form.d.ts +37 -37
- package/dist/features/seller/schemas/coupon-form.js +36 -8
- package/dist/schemas/registry.d.ts +10 -10
- package/package.json +1 -1
|
@@ -17,6 +17,16 @@ import { apiClient } from "../../../http";
|
|
|
17
17
|
const PAGE_SIZE = 50;
|
|
18
18
|
const FILTER_KEYS = ["active"];
|
|
19
19
|
const DEFAULT_SORT = "order";
|
|
20
|
+
function extractCarouselRows(response) {
|
|
21
|
+
if (Array.isArray(response))
|
|
22
|
+
return response;
|
|
23
|
+
if (!response || typeof response !== "object")
|
|
24
|
+
return undefined;
|
|
25
|
+
const obj = response;
|
|
26
|
+
if (Array.isArray(obj.data))
|
|
27
|
+
return obj.data;
|
|
28
|
+
return Array.isArray(obj.items) ? obj.items : undefined;
|
|
29
|
+
}
|
|
20
30
|
export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
21
31
|
const hasChildren = React.Children.count(children) > 0;
|
|
22
32
|
const [draggingId, setDraggingId] = useState(null);
|
|
@@ -30,7 +40,7 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
30
40
|
queryKey: ["admin", "carousel", "listing"],
|
|
31
41
|
endpoint: `${HOMEPAGE_ENDPOINTS.CAROUSEL}?includeInactive=true`,
|
|
32
42
|
mapRows: (response) => {
|
|
33
|
-
const sourceItems =
|
|
43
|
+
const sourceItems = extractCarouselRows(response);
|
|
34
44
|
return toRecordArray(sourceItems)
|
|
35
45
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
36
46
|
.map((item, index) => {
|
|
@@ -50,7 +60,13 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
|
50
60
|
};
|
|
51
61
|
});
|
|
52
62
|
},
|
|
53
|
-
getTotal: (response, mappedRows) =>
|
|
63
|
+
getTotal: (response, mappedRows) => {
|
|
64
|
+
if (Array.isArray(response) || !response || typeof response !== "object") {
|
|
65
|
+
return mappedRows.length;
|
|
66
|
+
}
|
|
67
|
+
const total = response.total;
|
|
68
|
+
return typeof total === "number" ? total : mappedRows.length;
|
|
69
|
+
},
|
|
54
70
|
buildFilters: (filterState) => {
|
|
55
71
|
const activeRaw = filterState.active;
|
|
56
72
|
if (activeRaw === "true")
|
|
@@ -9,6 +9,24 @@ import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdmin
|
|
|
9
9
|
import { DataListingView } from "./DataListingView";
|
|
10
10
|
import { AdminCategoryEditorView } from "./AdminCategoryEditorView";
|
|
11
11
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
12
|
+
/** Rows, whichever of the three shapes the endpoint returns. */
|
|
13
|
+
function extractCategoryRows(response) {
|
|
14
|
+
if (Array.isArray(response))
|
|
15
|
+
return response;
|
|
16
|
+
if (!response || typeof response !== "object")
|
|
17
|
+
return undefined;
|
|
18
|
+
const obj = response;
|
|
19
|
+
if (Array.isArray(obj.data))
|
|
20
|
+
return obj.data;
|
|
21
|
+
return Array.isArray(obj.items) ? obj.items : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** `total` only exists on the object shapes; a bare array has none. */
|
|
24
|
+
function extractCategoryTotal(response) {
|
|
25
|
+
if (Array.isArray(response) || !response || typeof response !== "object")
|
|
26
|
+
return undefined;
|
|
27
|
+
const total = response.total;
|
|
28
|
+
return typeof total === "number" ? total : undefined;
|
|
29
|
+
}
|
|
12
30
|
const COLUMNS = [
|
|
13
31
|
{
|
|
14
32
|
key: "primary",
|
|
@@ -46,7 +64,7 @@ const ADMIN_CATEGORIES_CONFIG = {
|
|
|
46
64
|
],
|
|
47
65
|
columns: COLUMNS,
|
|
48
66
|
mapRows: (response) => {
|
|
49
|
-
const sourceItems =
|
|
67
|
+
const sourceItems = extractCategoryRows(response);
|
|
50
68
|
return toRecordArray(sourceItems).map((item, index) => ({
|
|
51
69
|
id: toStringValue(item.id, `category-${index}`),
|
|
52
70
|
primary: toStringValue(item.name, "Untitled category"),
|
|
@@ -59,7 +77,7 @@ const ADMIN_CATEGORIES_CONFIG = {
|
|
|
59
77
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
60
78
|
}));
|
|
61
79
|
},
|
|
62
|
-
getTotal: (response, mappedRows) =>
|
|
80
|
+
getTotal: (response, mappedRows) => extractCategoryTotal(response) ?? mappedRows.length,
|
|
63
81
|
buildFilters: (state) => {
|
|
64
82
|
const parts = [];
|
|
65
83
|
if (state.isActive)
|
|
@@ -38,7 +38,7 @@ function FeaturedBundleCard({ bundle, showSavingsBadge = true, onBuyNow, }) {
|
|
|
38
38
|
const cover = bundle.display?.coverImage;
|
|
39
39
|
const href = String(ROUTES.PUBLIC.BUNDLE_DETAIL?.(bundle.slug) ?? "#");
|
|
40
40
|
const discount = computeBundleDiscount(bundle.bundlePrice, bundle.bundleOriginalTotal);
|
|
41
|
-
return (_jsxs(Stack, { gap: "none", surface: "default", rounded: "xl", border: "default", children: [_jsxs(Link, { href: href, className: "group block p-[var(--appkit-space-3)] hover:no-underline", children: [_jsxs(Div, { className: `relative mb-2 aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: [cover ? (_jsx(MediaImage, { src: cover, alt: bundle.name, size: "card", className: "transition-transform group-hover:scale-105" })) : (_jsx(Row, { textSize: "3xl", className: "h-full w-full", align: "center", justify: "center", children: PLACEHOLDER_EMOJI })), showSavingsBadge && discount && (_jsx(Badge, { variant: "success", className: "absolute left-2 top-2", children: BUNDLE_COPY.detail.discountBadge(discount.percent) }))] }), _jsx(Text, { className: "line-clamp-2", size: "sm", weight: "semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", wrap: true, children: [_jsx(Text, { size: "sm", weight: "bold", children: _jsx(GatedPrice, { label: "Sign in to see the bundle price", children: bundle.bundlePrice
|
|
41
|
+
return (_jsxs(Stack, { gap: "none", surface: "default", rounded: "xl", border: "default", children: [_jsxs(Link, { href: href, className: "group block p-[var(--appkit-space-3)] hover:no-underline", children: [_jsxs(Div, { className: `relative mb-2 aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: [cover ? (_jsx(MediaImage, { src: cover, alt: bundle.name, size: "card", className: "transition-transform group-hover:scale-105" })) : (_jsx(Row, { textSize: "3xl", className: "h-full w-full", align: "center", justify: "center", children: PLACEHOLDER_EMOJI })), showSavingsBadge && discount && (_jsx(PricesOnly, { children: _jsx(Badge, { variant: "success", className: "absolute left-2 top-2", children: BUNDLE_COPY.detail.discountBadge(discount.percent) }) }))] }), _jsx(Text, { className: "line-clamp-2", size: "sm", weight: "semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", wrap: true, children: [_jsx(Text, { size: "sm", weight: "bold", children: _jsx(GatedPrice, { label: "Sign in to see the bundle price", children: bundle.bundlePrice
|
|
42
42
|
? formatCurrency(bundle.bundlePrice, "INR")
|
|
43
43
|
: BUNDLE_COPY.featured.priceFallback }) }), discount && (_jsx(PricesOnly, { children: _jsx(Text, { size: "xs", color: "muted", className: "line-through", children: formatCurrency(discount.originalTotal, "INR") }) })), _jsxs(Text, { size: "xs", color: "muted", children: ["\u00B7 ", BUNDLE_COPY.featured.itemCount(memberCount)] }), stock !== "in_stock" && (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[stock], children: BUNDLE_COPY.stockBadge.listVariantOutOfStock }))] })] }), onBuyNow && (_jsx(Div, { border: "subtle", className: `border-t ${__P.p3}`, padding: "t-xs", children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: stock === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }));
|
|
44
44
|
}
|
|
@@ -10,7 +10,7 @@ const CLS_BUNDLE_PILL = "inline-flex items-center rounded-full bg-violet-600 px-
|
|
|
10
10
|
const CLS_STOCK_OK = "bg-success-solid text-success-on-solid";
|
|
11
11
|
// audit-content-alignment-ok: button label text, not page content — buttons are universally center-labeled
|
|
12
12
|
const CLS_VIEW_BTN = "mt-2 w-full cursor-pointer rounded-md bg-violet-600 py-[var(--appkit-space-1-5)] text-center text-[length:var(--appkit-text-xs)] font-semibold text-white transition-colors hover:bg-violet-700 active:scale-[0.98]";
|
|
13
|
-
import { BaseListingCard, Div, GatedPrice, Row, Span, Stack, Text, TextLink } from "../../../ui";
|
|
13
|
+
import { BaseListingCard, Div, GatedPrice, PricesOnly, Row, Span, Stack, Text, TextLink } from "../../../ui";
|
|
14
14
|
import { MediaImage } from "../../media/MediaImage";
|
|
15
15
|
import { computeBundleDiscount } from "../../../_internal/shared/features/categories/bundle-pricing";
|
|
16
16
|
import { BUNDLE_COPY } from "../../../_internal/shared/features/categories/bundle-copy";
|
|
@@ -49,7 +49,7 @@ export function MarketplaceBundleCard({ bundle, className = "", variant = "grid"
|
|
|
49
49
|
}
|
|
50
50
|
router.push(String(detailHref));
|
|
51
51
|
}, [detailHref, onNavigate, router]);
|
|
52
|
-
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, onTouchCancel: !isSelected ? longPress.onTouchCancel : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: showCollage ? (_jsx(Div, { layout: "grid", className: `h-full w-full gap-[0.125rem] bg-[var(--appkit-color-surface-muted)] ${collageTiles.length === 2 ? "grid-cols-2 grid-rows-1" : "grid-cols-2 grid-rows-2"}`, children: collageTiles.map((tile, i) => (_jsxs(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: [_jsx(MediaImage, { src: tile.imageURL, alt: tile.title ?? `${bundle.name} item ${i + 1}`, size: "card" }), i === collageTiles.length - 1 && overflow > 0 && (_jsxs(Row, { textWeight: "semibold", textSize: "sm", surface: "overlay-md", className: "absolute inset-0 text-white", align: "center", justify: "center", children: ["+", overflow] }))] }, `${tile.productId}-${i}`))) })) : cover ? (_jsx(Div, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", children: _jsx(MediaImage, { src: cover, alt: bundle.name, size: "card" }) })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No image" }) })) }), discount && (_jsx(Span, { size: "xs", weight: "bold", className: "absolute left-2 top-2 bg-success-solid text-success-on-solid", rounded: "full", padding: "pill-xs", children: BUNDLE_COPY.detail.discountBadge(discount.percent) })), _jsxs(Stack, { className: "absolute right-2 top-2", align: "end", gap: "xs", children: [_jsx(Span, { size: "xs", weight: "medium", className: CLS_BUNDLE_PILL, children: mergedLabels.bundleBadge }), _jsx(Span, { layout: "inline-flex", color: "inverse", className: `${stock === "out_of_stock" ? "bg-neutral-500 " : CLS_STOCK_OK}`, rounded: "full", padding: "pill-xs", size: "xs", weight: "medium", children: stock === "out_of_stock" ? mergedLabels.outOfStockBadge : mergedLabels.inStockBadge })] }), stock === "out_of_stock" && (_jsx(Row, { surface: "overlay-xs", className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Span, { color: "inverse", size: "xs", weight: "bold", className: "bg-neutral-900/80 tracking-wider", rounded: "default", padding: "pill-md", transform: "uppercase", children: mergedLabels.outOfStockBadge }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
52
|
+
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, onTouchCancel: !isSelected ? longPress.onTouchCancel : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: String(detailHref), className: "absolute inset-0 block", children: showCollage ? (_jsx(Div, { layout: "grid", className: `h-full w-full gap-[0.125rem] bg-[var(--appkit-color-surface-muted)] ${collageTiles.length === 2 ? "grid-cols-2 grid-rows-1" : "grid-cols-2 grid-rows-2"}`, children: collageTiles.map((tile, i) => (_jsxs(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: [_jsx(MediaImage, { src: tile.imageURL, alt: tile.title ?? `${bundle.name} item ${i + 1}`, size: "card" }), i === collageTiles.length - 1 && overflow > 0 && (_jsxs(Row, { textWeight: "semibold", textSize: "sm", surface: "overlay-md", className: "absolute inset-0 text-white", align: "center", justify: "center", children: ["+", overflow] }))] }, `${tile.productId}-${i}`))) })) : cover ? (_jsx(Div, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", children: _jsx(MediaImage, { src: cover, alt: bundle.name, size: "card" }) })) : (_jsx(Row, { className: "absolute inset-0 bg-[var(--appkit-color-surface-muted)]", align: "center", justify: "center", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "No image" }) })) }), discount && (_jsx(PricesOnly, { children: _jsx(Span, { size: "xs", weight: "bold", className: "absolute left-2 top-2 bg-success-solid text-success-on-solid", rounded: "full", padding: "pill-xs", children: BUNDLE_COPY.detail.discountBadge(discount.percent) }) })), _jsxs(Stack, { className: "absolute right-2 top-2", align: "end", gap: "xs", children: [_jsx(Span, { size: "xs", weight: "medium", className: CLS_BUNDLE_PILL, children: mergedLabels.bundleBadge }), _jsx(Span, { layout: "inline-flex", color: "inverse", className: `${stock === "out_of_stock" ? "bg-neutral-500 " : CLS_STOCK_OK}`, rounded: "full", padding: "pill-xs", size: "xs", weight: "medium", children: stock === "out_of_stock" ? mergedLabels.outOfStockBadge : mergedLabels.inStockBadge })] }), stock === "out_of_stock" && (_jsx(Row, { surface: "overlay-xs", className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Span, { color: "inverse", size: "xs", weight: "bold", className: "bg-neutral-900/80 tracking-wider", rounded: "default", padding: "pill-md", transform: "uppercase", children: mergedLabels.outOfStockBadge }) })), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
53
53
|
event.stopPropagation();
|
|
54
54
|
onSelect(bundle.id, !isSelected);
|
|
55
55
|
}, className: selectable || isSelected
|
|
@@ -15,9 +15,9 @@ import { z } from "zod";
|
|
|
15
15
|
export declare const couponFormBase: z.ZodObject<{
|
|
16
16
|
code: z.ZodString;
|
|
17
17
|
type: z.ZodEnum<["percentage", "fixed", "free_shipping"]>;
|
|
18
|
-
value: z.ZodEffects<z.ZodString, string, string
|
|
18
|
+
value: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
19
19
|
minPurchase: z.ZodEffects<z.ZodString, string, string>;
|
|
20
|
-
maxDiscount: z.ZodEffects<z.ZodString, string, string
|
|
20
|
+
maxDiscount: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
21
21
|
totalLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
22
22
|
perUserLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
23
23
|
startDate: z.ZodString;
|
|
@@ -26,31 +26,31 @@ export declare const couponFormBase: z.ZodObject<{
|
|
|
26
26
|
applicableProducts: z.ZodArray<z.ZodString, "many">;
|
|
27
27
|
applicableCategories: z.ZodArray<z.ZodString, "many">;
|
|
28
28
|
}, "strip", z.ZodTypeAny, {
|
|
29
|
-
value: string;
|
|
30
29
|
code: string;
|
|
31
30
|
type: "fixed" | "percentage" | "free_shipping";
|
|
32
31
|
isActive: boolean;
|
|
33
32
|
endDate: string;
|
|
34
|
-
maxDiscount: string;
|
|
35
33
|
minPurchase: string;
|
|
36
34
|
applicableProducts: string[];
|
|
37
35
|
applicableCategories: string[];
|
|
38
36
|
totalLimit: string;
|
|
39
37
|
perUserLimit: string;
|
|
40
38
|
startDate: string;
|
|
39
|
+
value?: string | undefined;
|
|
40
|
+
maxDiscount?: string | undefined;
|
|
41
41
|
}, {
|
|
42
|
-
value: string;
|
|
43
42
|
code: string;
|
|
44
43
|
type: "fixed" | "percentage" | "free_shipping";
|
|
45
44
|
isActive: boolean;
|
|
46
45
|
endDate: string;
|
|
47
|
-
maxDiscount: string;
|
|
48
46
|
minPurchase: string;
|
|
49
47
|
applicableProducts: string[];
|
|
50
48
|
applicableCategories: string[];
|
|
51
49
|
totalLimit: string;
|
|
52
50
|
perUserLimit: string;
|
|
53
51
|
startDate: string;
|
|
52
|
+
value?: string | undefined;
|
|
53
|
+
maxDiscount?: string | undefined;
|
|
54
54
|
}>;
|
|
55
55
|
/**
|
|
56
56
|
* The cross-field rules both editors need. Shared for the same reason the
|
|
@@ -59,7 +59,7 @@ export declare const couponFormBase: z.ZodObject<{
|
|
|
59
59
|
*/
|
|
60
60
|
export declare function refineCouponCommon(v: {
|
|
61
61
|
type: string;
|
|
62
|
-
value
|
|
62
|
+
value?: string;
|
|
63
63
|
startDate: string;
|
|
64
64
|
endDate: string;
|
|
65
65
|
totalLimit: string;
|
|
@@ -68,9 +68,9 @@ export declare function refineCouponCommon(v: {
|
|
|
68
68
|
export declare const sellerCouponFormSchema: z.ZodEffects<z.ZodObject<{
|
|
69
69
|
code: z.ZodString;
|
|
70
70
|
type: z.ZodEnum<["percentage", "fixed", "free_shipping"]>;
|
|
71
|
-
value: z.ZodEffects<z.ZodString, string, string
|
|
71
|
+
value: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
72
72
|
minPurchase: z.ZodEffects<z.ZodString, string, string>;
|
|
73
|
-
maxDiscount: z.ZodEffects<z.ZodString, string, string
|
|
73
|
+
maxDiscount: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
74
74
|
totalLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
75
75
|
perUserLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
76
76
|
startDate: z.ZodString;
|
|
@@ -79,57 +79,57 @@ export declare const sellerCouponFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
79
79
|
applicableProducts: z.ZodArray<z.ZodString, "many">;
|
|
80
80
|
applicableCategories: z.ZodArray<z.ZodString, "many">;
|
|
81
81
|
}, "strip", z.ZodTypeAny, {
|
|
82
|
-
value: string;
|
|
83
82
|
code: string;
|
|
84
83
|
type: "fixed" | "percentage" | "free_shipping";
|
|
85
84
|
isActive: boolean;
|
|
86
85
|
endDate: string;
|
|
87
|
-
maxDiscount: string;
|
|
88
86
|
minPurchase: string;
|
|
89
87
|
applicableProducts: string[];
|
|
90
88
|
applicableCategories: string[];
|
|
91
89
|
totalLimit: string;
|
|
92
90
|
perUserLimit: string;
|
|
93
91
|
startDate: string;
|
|
92
|
+
value?: string | undefined;
|
|
93
|
+
maxDiscount?: string | undefined;
|
|
94
94
|
}, {
|
|
95
|
-
value: string;
|
|
96
95
|
code: string;
|
|
97
96
|
type: "fixed" | "percentage" | "free_shipping";
|
|
98
97
|
isActive: boolean;
|
|
99
98
|
endDate: string;
|
|
100
|
-
maxDiscount: string;
|
|
101
99
|
minPurchase: string;
|
|
102
100
|
applicableProducts: string[];
|
|
103
101
|
applicableCategories: string[];
|
|
104
102
|
totalLimit: string;
|
|
105
103
|
perUserLimit: string;
|
|
106
104
|
startDate: string;
|
|
105
|
+
value?: string | undefined;
|
|
106
|
+
maxDiscount?: string | undefined;
|
|
107
107
|
}>, {
|
|
108
|
-
value: string;
|
|
109
108
|
code: string;
|
|
110
109
|
type: "fixed" | "percentage" | "free_shipping";
|
|
111
110
|
isActive: boolean;
|
|
112
111
|
endDate: string;
|
|
113
|
-
maxDiscount: string;
|
|
114
112
|
minPurchase: string;
|
|
115
113
|
applicableProducts: string[];
|
|
116
114
|
applicableCategories: string[];
|
|
117
115
|
totalLimit: string;
|
|
118
116
|
perUserLimit: string;
|
|
119
117
|
startDate: string;
|
|
118
|
+
value?: string | undefined;
|
|
119
|
+
maxDiscount?: string | undefined;
|
|
120
120
|
}, {
|
|
121
|
-
value: string;
|
|
122
121
|
code: string;
|
|
123
122
|
type: "fixed" | "percentage" | "free_shipping";
|
|
124
123
|
isActive: boolean;
|
|
125
124
|
endDate: string;
|
|
126
|
-
maxDiscount: string;
|
|
127
125
|
minPurchase: string;
|
|
128
126
|
applicableProducts: string[];
|
|
129
127
|
applicableCategories: string[];
|
|
130
128
|
totalLimit: string;
|
|
131
129
|
perUserLimit: string;
|
|
132
130
|
startDate: string;
|
|
131
|
+
value?: string | undefined;
|
|
132
|
+
maxDiscount?: string | undefined;
|
|
133
133
|
}>;
|
|
134
134
|
export type SellerCouponFormValues = z.infer<typeof sellerCouponFormSchema>;
|
|
135
135
|
/**
|
|
@@ -183,9 +183,9 @@ export declare function couponDraftToPayload(draft: SellerCouponFormValues): Sel
|
|
|
183
183
|
*/
|
|
184
184
|
export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
|
|
185
185
|
code: z.ZodString;
|
|
186
|
-
value: z.ZodEffects<z.ZodString, string, string
|
|
186
|
+
value: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
187
187
|
minPurchase: z.ZodEffects<z.ZodString, string, string>;
|
|
188
|
-
maxDiscount: z.ZodEffects<z.ZodString, string, string
|
|
188
|
+
maxDiscount: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
189
189
|
totalLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
190
190
|
perUserLimit: z.ZodEffects<z.ZodString, string, string>;
|
|
191
191
|
startDate: z.ZodString;
|
|
@@ -197,90 +197,90 @@ export declare const adminCouponFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
197
197
|
type: z.ZodEnum<["percentage", "fixed", "free_shipping", "buy_x_get_y"]>;
|
|
198
198
|
name: z.ZodString;
|
|
199
199
|
description: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
|
|
200
|
-
buyQuantity: z.ZodEffects<z.ZodString, string, string
|
|
201
|
-
getQuantity: z.ZodEffects<z.ZodString, string, string
|
|
200
|
+
buyQuantity: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
201
|
+
getQuantity: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
202
202
|
currentUsage: z.ZodOptional<z.ZodNumber>;
|
|
203
203
|
firstTimeUserOnly: z.ZodBoolean;
|
|
204
204
|
appliesToAuctions: z.ZodBoolean;
|
|
205
205
|
}, "strip", z.ZodTypeAny, {
|
|
206
|
-
value: string;
|
|
207
206
|
code: string;
|
|
208
207
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
209
208
|
name: string;
|
|
210
209
|
isActive: boolean;
|
|
211
210
|
endDate: string;
|
|
212
211
|
firstTimeUserOnly: boolean;
|
|
213
|
-
maxDiscount: string;
|
|
214
212
|
minPurchase: string;
|
|
215
|
-
buyQuantity: string;
|
|
216
|
-
getQuantity: string;
|
|
217
213
|
applicableProducts: string[];
|
|
218
214
|
applicableCategories: string[];
|
|
219
215
|
totalLimit: string;
|
|
220
216
|
perUserLimit: string;
|
|
221
217
|
startDate: string;
|
|
222
218
|
appliesToAuctions: boolean;
|
|
219
|
+
value?: string | undefined;
|
|
223
220
|
description?: string | undefined;
|
|
224
221
|
currentUsage?: number | undefined;
|
|
222
|
+
maxDiscount?: string | undefined;
|
|
223
|
+
buyQuantity?: string | undefined;
|
|
224
|
+
getQuantity?: string | undefined;
|
|
225
225
|
}, {
|
|
226
|
-
value: string;
|
|
227
226
|
code: string;
|
|
228
227
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
229
228
|
name: string;
|
|
230
229
|
isActive: boolean;
|
|
231
230
|
endDate: string;
|
|
232
231
|
firstTimeUserOnly: boolean;
|
|
233
|
-
maxDiscount: string;
|
|
234
232
|
minPurchase: string;
|
|
235
|
-
buyQuantity: string;
|
|
236
|
-
getQuantity: string;
|
|
237
233
|
applicableProducts: string[];
|
|
238
234
|
applicableCategories: string[];
|
|
239
235
|
totalLimit: string;
|
|
240
236
|
perUserLimit: string;
|
|
241
237
|
startDate: string;
|
|
242
238
|
appliesToAuctions: boolean;
|
|
239
|
+
value?: string | undefined;
|
|
243
240
|
description?: string | undefined;
|
|
244
241
|
currentUsage?: number | undefined;
|
|
242
|
+
maxDiscount?: string | undefined;
|
|
243
|
+
buyQuantity?: string | undefined;
|
|
244
|
+
getQuantity?: string | undefined;
|
|
245
245
|
}>, {
|
|
246
|
-
value: string;
|
|
247
246
|
code: string;
|
|
248
247
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
249
248
|
name: string;
|
|
250
249
|
isActive: boolean;
|
|
251
250
|
endDate: string;
|
|
252
251
|
firstTimeUserOnly: boolean;
|
|
253
|
-
maxDiscount: string;
|
|
254
252
|
minPurchase: string;
|
|
255
|
-
buyQuantity: string;
|
|
256
|
-
getQuantity: string;
|
|
257
253
|
applicableProducts: string[];
|
|
258
254
|
applicableCategories: string[];
|
|
259
255
|
totalLimit: string;
|
|
260
256
|
perUserLimit: string;
|
|
261
257
|
startDate: string;
|
|
262
258
|
appliesToAuctions: boolean;
|
|
259
|
+
value?: string | undefined;
|
|
263
260
|
description?: string | undefined;
|
|
264
261
|
currentUsage?: number | undefined;
|
|
262
|
+
maxDiscount?: string | undefined;
|
|
263
|
+
buyQuantity?: string | undefined;
|
|
264
|
+
getQuantity?: string | undefined;
|
|
265
265
|
}, {
|
|
266
|
-
value: string;
|
|
267
266
|
code: string;
|
|
268
267
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
269
268
|
name: string;
|
|
270
269
|
isActive: boolean;
|
|
271
270
|
endDate: string;
|
|
272
271
|
firstTimeUserOnly: boolean;
|
|
273
|
-
maxDiscount: string;
|
|
274
272
|
minPurchase: string;
|
|
275
|
-
buyQuantity: string;
|
|
276
|
-
getQuantity: string;
|
|
277
273
|
applicableProducts: string[];
|
|
278
274
|
applicableCategories: string[];
|
|
279
275
|
totalLimit: string;
|
|
280
276
|
perUserLimit: string;
|
|
281
277
|
startDate: string;
|
|
282
278
|
appliesToAuctions: boolean;
|
|
279
|
+
value?: string | undefined;
|
|
283
280
|
description?: string | undefined;
|
|
284
281
|
currentUsage?: number | undefined;
|
|
282
|
+
maxDiscount?: string | undefined;
|
|
283
|
+
buyQuantity?: string | undefined;
|
|
284
|
+
getQuantity?: string | undefined;
|
|
285
285
|
}>;
|
|
286
286
|
export type AdminCouponFormValues = z.infer<typeof adminCouponFormSchema>;
|
|
@@ -70,7 +70,10 @@ export const couponFormBase = z
|
|
|
70
70
|
* `when` carries what the editor held as JSX conditionals, so a field the
|
|
71
71
|
* form hides is one the payload cannot carry — see `couponDraftToPayload`.
|
|
72
72
|
*/
|
|
73
|
-
|
|
73
|
+
// Optional for the same reason as buyQuantity/getQuantity below: this field
|
|
74
|
+
// is `when`-gated, so visibleValues() strips it on a free-shipping coupon.
|
|
75
|
+
// refineCouponCommon still requires it for every other type.
|
|
76
|
+
value: annotate(numericString("The discount value").optional(), {
|
|
74
77
|
section: "basics", quick: true, order: 3, row: "pair",
|
|
75
78
|
when: (v) => v.type !== "free_shipping",
|
|
76
79
|
}),
|
|
@@ -83,7 +86,9 @@ export const couponFormBase = z
|
|
|
83
86
|
* cap typed while the type was "percentage" persisted after switching to
|
|
84
87
|
* free shipping, invisibly.
|
|
85
88
|
*/
|
|
86
|
-
|
|
89
|
+
// `when`-gated to percentage only, so it is stripped for fixed and
|
|
90
|
+
// free-shipping coupons — optional, or those two types cannot be saved.
|
|
91
|
+
maxDiscount: annotate(numericString("Maximum discount").optional(), {
|
|
87
92
|
section: "limits", order: 2, row: "pair",
|
|
88
93
|
when: (v) => v.type === "percentage",
|
|
89
94
|
}),
|
|
@@ -121,8 +126,9 @@ export function refineCouponCommon(v, ctx) {
|
|
|
121
126
|
// a percentage above 100 is not a discount — it is the store paying the
|
|
122
127
|
// customer. Nothing bounded this before.
|
|
123
128
|
if (v.type !== "free_shipping") {
|
|
124
|
-
const
|
|
125
|
-
|
|
129
|
+
const rawValue = v.value ?? "";
|
|
130
|
+
const amount = Number(rawValue);
|
|
131
|
+
if (rawValue.trim() === "")
|
|
126
132
|
issue("value", "A discount value is required.");
|
|
127
133
|
else if (amount <= 0)
|
|
128
134
|
issue("value", "The discount must be greater than zero.");
|
|
@@ -205,11 +211,28 @@ export const adminCouponFormSchema = couponFormBase
|
|
|
205
211
|
description: annotate(z.string().max(500).optional().or(z.literal("")), {
|
|
206
212
|
section: "basics", order: 5, row: "full", kind: "textarea",
|
|
207
213
|
}),
|
|
208
|
-
|
|
214
|
+
/*
|
|
215
|
+
* 🛑 A `when`-gated field MUST be optional, or the form cannot be submitted
|
|
216
|
+
* at all while the condition is false.
|
|
217
|
+
*
|
|
218
|
+
* `visibleValues()` strips every field whose `when` is false before the
|
|
219
|
+
* submit path parses the draft — that is its whole job, so a hidden input
|
|
220
|
+
* cannot smuggle a stale value into the payload. But a stripped key is
|
|
221
|
+
* `undefined` to Zod, and these were required strings, so editing any
|
|
222
|
+
* ordinary percentage coupon produced exactly two "This field is required"
|
|
223
|
+
* issues for the two buy-x-get-y inputs that are not on screen. The summary
|
|
224
|
+
* rendered them as "Coupon: This field is required" twice, naming the
|
|
225
|
+
* section rather than any field, and no PATCH was ever issued.
|
|
226
|
+
*
|
|
227
|
+
* Optional here is not a loosening: the superRefine below still demands a
|
|
228
|
+
* whole number of one or more whenever the type IS buy_x_get_y, which is
|
|
229
|
+
* the only time either field is real.
|
|
230
|
+
*/
|
|
231
|
+
buyQuantity: annotate(numericString("Buy quantity").optional(), {
|
|
209
232
|
section: "basics", order: 6, row: "pair", kind: "number",
|
|
210
233
|
when: (v) => v.type === "buy_x_get_y",
|
|
211
234
|
}),
|
|
212
|
-
getQuantity: annotate(numericString("Get quantity"), {
|
|
235
|
+
getQuantity: annotate(numericString("Get quantity").optional(), {
|
|
213
236
|
section: "basics", order: 7, row: "pair", kind: "number",
|
|
214
237
|
when: (v) => v.type === "buy_x_get_y",
|
|
215
238
|
}),
|
|
@@ -235,8 +258,13 @@ export const adminCouponFormSchema = couponFormBase
|
|
|
235
258
|
// A buy-x-get-y with a zero on either side is not an offer.
|
|
236
259
|
if (v.type === "buy_x_get_y") {
|
|
237
260
|
for (const key of ["buyQuantity", "getQuantity"]) {
|
|
238
|
-
|
|
239
|
-
|
|
261
|
+
// `?? ""` because the field is optional now — see the note on its
|
|
262
|
+
// declaration. When the type IS buy_x_get_y the input is on screen, so
|
|
263
|
+
// an absent value here means the admin left it blank, which this
|
|
264
|
+
// rejects exactly as it did before.
|
|
265
|
+
const raw = v[key] ?? "";
|
|
266
|
+
const n = Number(raw);
|
|
267
|
+
if (raw.trim() === "" || !Number.isFinite(n) || n < 1) {
|
|
240
268
|
ctx.addIssue({
|
|
241
269
|
code: z.ZodIssueCode.custom,
|
|
242
270
|
path: [key],
|
|
@@ -4755,9 +4755,9 @@ export declare const SCHEMAS: {
|
|
|
4755
4755
|
readonly sellerCoupon: import("zod").ZodEffects<import("zod").ZodObject<{
|
|
4756
4756
|
code: import("zod").ZodString;
|
|
4757
4757
|
type: import("zod").ZodEnum<["percentage", "fixed", "free_shipping"]>;
|
|
4758
|
-
value: import("zod").ZodEffects<import("zod").ZodString, string, string
|
|
4758
|
+
value: import("zod").ZodOptional<import("zod").ZodEffects<import("zod").ZodString, string, string>>;
|
|
4759
4759
|
minPurchase: import("zod").ZodEffects<import("zod").ZodString, string, string>;
|
|
4760
|
-
maxDiscount: import("zod").ZodEffects<import("zod").ZodString, string, string
|
|
4760
|
+
maxDiscount: import("zod").ZodOptional<import("zod").ZodEffects<import("zod").ZodString, string, string>>;
|
|
4761
4761
|
totalLimit: import("zod").ZodEffects<import("zod").ZodString, string, string>;
|
|
4762
4762
|
perUserLimit: import("zod").ZodEffects<import("zod").ZodString, string, string>;
|
|
4763
4763
|
startDate: import("zod").ZodString;
|
|
@@ -4766,57 +4766,57 @@ export declare const SCHEMAS: {
|
|
|
4766
4766
|
applicableProducts: import("zod").ZodArray<import("zod").ZodString, "many">;
|
|
4767
4767
|
applicableCategories: import("zod").ZodArray<import("zod").ZodString, "many">;
|
|
4768
4768
|
}, "strip", import("zod").ZodTypeAny, {
|
|
4769
|
-
value: string;
|
|
4770
4769
|
code: string;
|
|
4771
4770
|
type: "fixed" | "percentage" | "free_shipping";
|
|
4772
4771
|
isActive: boolean;
|
|
4773
4772
|
endDate: string;
|
|
4774
|
-
maxDiscount: string;
|
|
4775
4773
|
minPurchase: string;
|
|
4776
4774
|
applicableProducts: string[];
|
|
4777
4775
|
applicableCategories: string[];
|
|
4778
4776
|
totalLimit: string;
|
|
4779
4777
|
perUserLimit: string;
|
|
4780
4778
|
startDate: string;
|
|
4779
|
+
value?: string | undefined;
|
|
4780
|
+
maxDiscount?: string | undefined;
|
|
4781
4781
|
}, {
|
|
4782
|
-
value: string;
|
|
4783
4782
|
code: string;
|
|
4784
4783
|
type: "fixed" | "percentage" | "free_shipping";
|
|
4785
4784
|
isActive: boolean;
|
|
4786
4785
|
endDate: string;
|
|
4787
|
-
maxDiscount: string;
|
|
4788
4786
|
minPurchase: string;
|
|
4789
4787
|
applicableProducts: string[];
|
|
4790
4788
|
applicableCategories: string[];
|
|
4791
4789
|
totalLimit: string;
|
|
4792
4790
|
perUserLimit: string;
|
|
4793
4791
|
startDate: string;
|
|
4792
|
+
value?: string | undefined;
|
|
4793
|
+
maxDiscount?: string | undefined;
|
|
4794
4794
|
}>, {
|
|
4795
|
-
value: string;
|
|
4796
4795
|
code: string;
|
|
4797
4796
|
type: "fixed" | "percentage" | "free_shipping";
|
|
4798
4797
|
isActive: boolean;
|
|
4799
4798
|
endDate: string;
|
|
4800
|
-
maxDiscount: string;
|
|
4801
4799
|
minPurchase: string;
|
|
4802
4800
|
applicableProducts: string[];
|
|
4803
4801
|
applicableCategories: string[];
|
|
4804
4802
|
totalLimit: string;
|
|
4805
4803
|
perUserLimit: string;
|
|
4806
4804
|
startDate: string;
|
|
4805
|
+
value?: string | undefined;
|
|
4806
|
+
maxDiscount?: string | undefined;
|
|
4807
4807
|
}, {
|
|
4808
|
-
value: string;
|
|
4809
4808
|
code: string;
|
|
4810
4809
|
type: "fixed" | "percentage" | "free_shipping";
|
|
4811
4810
|
isActive: boolean;
|
|
4812
4811
|
endDate: string;
|
|
4813
|
-
maxDiscount: string;
|
|
4814
4812
|
minPurchase: string;
|
|
4815
4813
|
applicableProducts: string[];
|
|
4816
4814
|
applicableCategories: string[];
|
|
4817
4815
|
totalLimit: string;
|
|
4818
4816
|
perUserLimit: string;
|
|
4819
4817
|
startDate: string;
|
|
4818
|
+
value?: string | undefined;
|
|
4819
|
+
maxDiscount?: string | undefined;
|
|
4820
4820
|
}>;
|
|
4821
4821
|
readonly customRole: import("zod").ZodObject<{
|
|
4822
4822
|
name: import("zod").ZodString;
|