@mohasinac/appkit 4.16.0 → 4.17.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/client.d.ts +5 -3
- package/dist/client.js +5 -3
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +18 -6
- package/dist/features/admin/components/BrandQuickCreateForm.js +13 -5
- package/dist/features/admin/components/CategoryQuickCreateForm.js +13 -5
- package/dist/features/admin/schemas/small-forms.d.ts +54 -0
- package/dist/features/admin/schemas/small-forms.js +85 -0
- package/dist/features/auth/components/ForgotPasswordView.js +2 -1
- package/dist/features/auth/components/LoginForm.js +2 -1
- package/dist/features/auth/components/RegisterForm.js +2 -1
- package/dist/features/auth/components/ResetPasswordView.js +2 -1
- package/dist/features/consultation/components/ConsultationForm.js +3 -1
- package/dist/features/consultation/schemas/index.d.ts +4 -4
- package/dist/features/consultation/schemas/index.js +22 -5
- package/dist/features/contact/schemas/inquiry-forms.d.ts +13 -0
- package/dist/features/contact/schemas/inquiry-forms.js +34 -0
- package/dist/features/corporate/components/CorporateInquiryForm.js +3 -1
- package/dist/features/corporate/schemas/index.d.ts +4 -1
- package/dist/features/corporate/schemas/index.js +38 -8
- package/dist/features/events/components/EventRaffleEntryForm.js +3 -1
- package/dist/features/homepage/components/NewsletterBanner.js +3 -1
- package/dist/features/products/components/MakeOfferButton.js +1 -1
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +27 -9
- package/dist/features/store-extensions/schemas/listing-template-form.d.ts +19 -1
- package/dist/features/store-extensions/schemas/listing-template-form.js +42 -1
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +19 -13
- package/dist/features/store-extensions/schemas/shipping-config-form.js +25 -2
- package/dist/features/store-extensions/schemas/store-category-form.d.ts +40 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +28 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/schemas/registry.d.ts +8 -5
- package/package.json +1 -1
|
@@ -64,17 +64,19 @@ export declare const submitCorporateInquirySchema: z.ZodObject<{
|
|
|
64
64
|
contactPerson: z.ZodString;
|
|
65
65
|
designation: z.ZodOptional<z.ZodString>;
|
|
66
66
|
email: z.ZodString;
|
|
67
|
-
phone: z.ZodOptional<z.ZodString>;
|
|
67
|
+
phone: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
|
|
68
68
|
units: z.ZodOptional<z.ZodNumber>;
|
|
69
69
|
budgetPerUnit: z.ZodOptional<z.ZodNumber>;
|
|
70
70
|
totalBudget: z.ZodOptional<z.ZodNumber>;
|
|
71
71
|
deliveryDateRequired: z.ZodOptional<z.ZodString>;
|
|
72
72
|
customBranding: z.ZodDefault<z.ZodBoolean>;
|
|
73
|
+
message: z.ZodOptional<z.ZodString>;
|
|
73
74
|
}, "strip", z.ZodTypeAny, {
|
|
74
75
|
email: string;
|
|
75
76
|
companyName: string;
|
|
76
77
|
contactPerson: string;
|
|
77
78
|
customBranding: boolean;
|
|
79
|
+
message?: string | undefined;
|
|
78
80
|
phone?: string | undefined;
|
|
79
81
|
designation?: string | undefined;
|
|
80
82
|
units?: number | undefined;
|
|
@@ -85,6 +87,7 @@ export declare const submitCorporateInquirySchema: z.ZodObject<{
|
|
|
85
87
|
email: string;
|
|
86
88
|
companyName: string;
|
|
87
89
|
contactPerson: string;
|
|
90
|
+
message?: string | undefined;
|
|
88
91
|
phone?: string | undefined;
|
|
89
92
|
designation?: string | undefined;
|
|
90
93
|
units?: number | undefined;
|
|
@@ -37,14 +37,44 @@ export const corporateInquirySchema = z.object({
|
|
|
37
37
|
* Form schema for submitting a corporate inquiry.
|
|
38
38
|
*/
|
|
39
39
|
export const submitCorporateInquirySchema = z.object({
|
|
40
|
-
companyName: z.string().min(2),
|
|
41
|
-
contactPerson: z.string().min(2),
|
|
42
|
-
designation: z.string().optional(),
|
|
43
|
-
email: z
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
companyName: z.string().trim().min(2, "A company name is required.").max(160),
|
|
41
|
+
contactPerson: z.string().trim().min(2, "Who should we reply to?").max(120),
|
|
42
|
+
designation: z.string().trim().max(120).optional(),
|
|
43
|
+
email: z
|
|
44
|
+
.string()
|
|
45
|
+
.trim()
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.min(1, "An email address is required.")
|
|
48
|
+
.email("That doesn't look like an email address."),
|
|
49
|
+
// Indian mobile, tolerant of spacing and a country code. Optional — a
|
|
50
|
+
// reachable email is enough, and demanding a phone number loses enquiries.
|
|
51
|
+
phone: z
|
|
52
|
+
.string()
|
|
53
|
+
.trim()
|
|
54
|
+
.regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
|
|
55
|
+
.optional()
|
|
56
|
+
.or(z.literal("")),
|
|
57
|
+
/*
|
|
58
|
+
* 🛑 COERCED. These were `z.number()`, and this schema's own form sends
|
|
59
|
+
* strings — so every submission carrying a unit count failed to parse the
|
|
60
|
+
* moment anyone actually wired the form to the schema. Bounds are
|
|
61
|
+
* deliberately generous: they catch a mistyped figure, not a large order.
|
|
62
|
+
*
|
|
63
|
+
* A sales reply is priced from these two, and until now both were free text.
|
|
64
|
+
*/
|
|
65
|
+
units: z.coerce
|
|
66
|
+
.number({ invalid_type_error: "Enter the number of units as a number." })
|
|
67
|
+
.int("Units must be a whole number.")
|
|
68
|
+
.positive("At least one unit.")
|
|
69
|
+
.max(1000000, "That looks like a typo — contact us directly for orders this large.")
|
|
70
|
+
.optional(),
|
|
71
|
+
budgetPerUnit: z.coerce
|
|
72
|
+
.number({ invalid_type_error: "Enter a per-unit budget as a number." })
|
|
73
|
+
.min(0, "A budget cannot be negative.")
|
|
74
|
+
.max(10000000, "That looks like a typo.")
|
|
75
|
+
.optional(),
|
|
76
|
+
totalBudget: z.coerce.number().min(0).optional(),
|
|
48
77
|
deliveryDateRequired: z.string().optional(),
|
|
49
78
|
customBranding: z.boolean().default(false),
|
|
79
|
+
message: z.string().trim().max(4000).optional(),
|
|
50
80
|
});
|
|
@@ -6,6 +6,8 @@ import { Form } from "../../../ui/components/Form";
|
|
|
6
6
|
import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
|
|
7
7
|
import { Div, Heading, Stack, Text } from "../../../ui";
|
|
8
8
|
import { Badge } from "../../../ui/components/Badge";
|
|
9
|
+
import { raffleEntrySchema } from "../../contact/schemas/inquiry-forms";
|
|
10
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
9
11
|
const __P = {
|
|
10
12
|
p6: "p-[var(--appkit-space-6)]",
|
|
11
13
|
};
|
|
@@ -38,5 +40,5 @@ export function EventRaffleEntryForm({ prizeLabel, raffleType = "open_raffle", t
|
|
|
38
40
|
: raffleType === "top_n_participants"
|
|
39
41
|
? `Earliest ${topN ?? "N"} entries qualify.`
|
|
40
42
|
: "Spin the wheel to claim your prize.";
|
|
41
|
-
return (_jsxs(Stack, { className: `${__P.p6}`, gap: "md", rounded: "2xl", border: "default", children: [prizeLabel ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Badge, { variant: "warning", children: "Prize" }), _jsx(Heading, { level: 2, weight: "bold", size: "2xl", children: prizeLabel })] })) : null, _jsx(Text, { size: "sm", color: "muted", children: eligibilityCopy }), submitted ? (_jsx(Div, { textSize: "sm", color: "success", surface: "success-surface", padding: "inline", rounded: "lg", children: "You're in. Check the leaderboard for results." })) : overLimit ? (_jsxs(Div, { textSize: "sm", color: "warning", surface: "warning-surface", padding: "inline", rounded: "lg", children: ["You've used all ", maxEntriesPerUser, " entry slot", maxEntriesPerUser === 1 ? "" : "s", " for this raffle."] })) : (_jsxs(Form, { onSubmit: handleSubmit, className: "space-y-[0.75rem]", children: [_jsx(FieldTextarea, { name: "message", label: "Message", hint: "optional \u2014 share why you'd like to win", value: message, onChange: setMessage, rows: 3, maxLength: 500, placeholder: "Share why you'd like to win", disabled: busy }), _jsx(Button, { type: "submit", variant: "primary", disabled: busy, children: busy ? "Submitting…" : "Enter raffle" })] }))] }));
|
|
43
|
+
return (_jsxs(Stack, { className: `${__P.p6}`, gap: "md", rounded: "2xl", border: "default", children: [prizeLabel ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Badge, { variant: "warning", children: "Prize" }), _jsx(Heading, { level: 2, weight: "bold", size: "2xl", children: prizeLabel })] })) : null, _jsx(Text, { size: "sm", color: "muted", children: eligibilityCopy }), submitted ? (_jsx(Div, { textSize: "sm", color: "success", surface: "success-surface", padding: "inline", rounded: "lg", children: "You're in. Check the leaderboard for results." })) : overLimit ? (_jsxs(Div, { textSize: "sm", color: "warning", surface: "warning-surface", padding: "inline", rounded: "lg", children: ["You've used all ", maxEntriesPerUser, " entry slot", maxEntriesPerUser === 1 ? "" : "s", " for this raffle."] })) : (_jsxs(Form, { schema: raffleEntrySchema, onSubmit: handleSubmit, className: "space-y-[0.75rem]", children: [_jsx(FormErrorSummary, {}), _jsx(FieldTextarea, { name: "message", label: "Message", hint: "optional \u2014 share why you'd like to win", value: message, onChange: setMessage, rows: 3, maxLength: 500, placeholder: "Share why you'd like to win", disabled: busy }), _jsx(Button, { type: "submit", variant: "primary", disabled: busy, children: busy ? "Submitting…" : "Enter raffle" })] }))] }));
|
|
42
44
|
}
|
|
@@ -6,6 +6,8 @@ import { Button, Div, Heading, Span, Text } from "../../../ui";
|
|
|
6
6
|
import { DynamicBgDiv } from "../../../ui/components/DynamicBgDiv";
|
|
7
7
|
import { Form } from "../../../ui/components/Form";
|
|
8
8
|
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
9
|
+
import { newsletterSubscribeSchema } from "../../admin/schemas/small-forms";
|
|
10
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
9
11
|
const CLS_ERROR_TEXT = "mt-3 text-[length:var(--appkit-text-sm)] text-error";
|
|
10
12
|
export function NewsletterBanner({ onSubscribe, eyebrow = "STAY CONNECTED", heading = "Join Our Newsletter", subheading = "Get exclusive offers, new arrivals and more.", placeholder = "Enter your email", ctaLabel = "Subscribe", successMessage = "You're in! Check your inbox.", errorMessage = "Something went wrong. Please try again.", }) {
|
|
11
13
|
const [email, setEmail] = useState("");
|
|
@@ -31,5 +33,5 @@ export function NewsletterBanner({ onSubscribe, eyebrow = "STAY CONNECTED", head
|
|
|
31
33
|
setLoading(false);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
return (_jsxs(DynamicBgDiv, { background: "var(--newsletter-bg, linear-gradient(135deg, var(--appkit-color-primary) 0%, var(--appkit-color-secondary) 100%))", className: "py-[var(--appkit-space-24)] relative overflow-hidden", children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 border border-white/[0.06]", rounded: "full" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 border border-dashed border-white/[0.08]", rounded: "full" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl text-left sm:px-[1.5rem]", padding: "x-md", children: [_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "mb-4 inline-flex border border-white/20 tracking-widest /70", rounded: "full", padding: "pill-md", transform: "uppercase", children: eyebrow }), _jsx(Heading, { color: "inverse", level: 2, smSize: "4xl", size: "3xl", weight: "bold", children: heading }), _jsx(Text, { color: "inverse", className: "mt-4 /60", children: subheading }), success ? (_jsx(Text, { paddingY: "md", color: "inverse", className: "mt-8 border border-white/20 bg-[rgba(255,255,255,0.1)]", paddingX: "x-lg", size: "sm", weight: "semibold", rounded: "xl", children: successMessage })) : (_jsxs(Form, { onSubmit: handleSubmit, className: "mt-8 flex gap-[0.75rem]", children: [_jsx(FieldInput, { name: "email", type: "email", value: email, onChange: setEmail, placeholder: placeholder, required: true, className: "flex-1" }), _jsx(Button, { rounded: "xl", type: "submit", disabled: loading, variant: "primary", paddingX: "lg", textSize: "sm", className: "shrink-0 py-[0.75rem] font-[700] transition-opacity disabled:opacity-60 bg-[var(--color-yellow)] text-black", children: loading ? "…" : ctaLabel })] })), error && (_jsx(Text, { className: CLS_ERROR_TEXT, children: errorMessage })), _jsx(Text, { color: "inverse", className: "mt-4 /40", size: "xs", children: "No spam, ever. Unsubscribe anytime." })] })] }));
|
|
36
|
+
return (_jsxs(DynamicBgDiv, { background: "var(--newsletter-bg, linear-gradient(135deg, var(--appkit-color-primary) 0%, var(--appkit-color-secondary) 100%))", className: "py-[var(--appkit-space-24)] relative overflow-hidden", children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 border border-white/[0.06]", rounded: "full" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 border border-dashed border-white/[0.08]", rounded: "full" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl text-left sm:px-[1.5rem]", padding: "x-md", children: [_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "mb-4 inline-flex border border-white/20 tracking-widest /70", rounded: "full", padding: "pill-md", transform: "uppercase", children: eyebrow }), _jsx(Heading, { color: "inverse", level: 2, smSize: "4xl", size: "3xl", weight: "bold", children: heading }), _jsx(Text, { color: "inverse", className: "mt-4 /60", children: subheading }), success ? (_jsx(Text, { paddingY: "md", color: "inverse", className: "mt-8 border border-white/20 bg-[rgba(255,255,255,0.1)]", paddingX: "x-lg", size: "sm", weight: "semibold", rounded: "xl", children: successMessage })) : (_jsxs(Form, { schema: newsletterSubscribeSchema, onSubmit: handleSubmit, className: "mt-8 flex gap-[0.75rem]", children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "email", type: "email", value: email, onChange: setEmail, placeholder: placeholder, required: true, className: "flex-1" }), _jsx(Button, { rounded: "xl", type: "submit", disabled: loading, variant: "primary", paddingX: "lg", textSize: "sm", className: "shrink-0 py-[0.75rem] font-[700] transition-opacity disabled:opacity-60 bg-[var(--color-yellow)] text-black", children: loading ? "…" : ctaLabel })] })), error && (_jsx(Text, { className: CLS_ERROR_TEXT, children: errorMessage })), _jsx(Text, { color: "inverse", className: "mt-4 /40", size: "xs", children: "No spam, ever. Unsubscribe anytime." })] })] }));
|
|
35
37
|
}
|
|
@@ -75,7 +75,7 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
|
|
|
75
75
|
if (state === "pending") {
|
|
76
76
|
return (_jsxs(Div, { className: `${CLS_PENDING_BOX} ${className}`, children: [_jsx(Span, { size: "lg", children: "\u23F3" }), _jsx(Text, { className: CLS_PENDING_TITLE, children: "Offer Pending" }), _jsx(Text, { className: CLS_PENDING_BODY, children: "You already have an offer on this item. Check My Offers for updates." })] }));
|
|
77
77
|
}
|
|
78
|
-
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
|
|
78
|
+
return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { schema: schema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
|
|
79
79
|
setOfferAmount(value);
|
|
80
80
|
clearErrors();
|
|
81
81
|
}, min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { size: "xs", color: "faint", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] }), _jsx(FieldInput, { name: "buyerNote", type: "text", label: "Note to seller (optional)", value: buyerNote, onChange: setBuyerNote, placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" }), _jsx(Text, { size: "xs", color: "muted", children: "The seller will accept, decline, or suggest a counter price." }), _jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Button, { action: ACTIONS.PRODUCT["make-offer"], variant: "primary", size: "sm", type: "button", isLoading: isPending, disabled: isPending, onClick: () => submit(setFieldError, clearErrors), children: isPending ? "Sending…" : `Send offer of ${fmt(Number(offerAmount) || 0)}` }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setState("idle"), disabled: isPending, children: "Cancel" })] })] })) }) })] }));
|
|
@@ -6,6 +6,9 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
6
6
|
import { Alert, Badge, Button, Div, Form, FormActions, Heading, Input, Row, Section, Select, Stack, Text, Toggle, useToast } from "../../../ui";
|
|
7
7
|
import { apiClient } from "../../../http";
|
|
8
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { analyticsAlertCreateSchema } from "../../store-extensions/schemas/analytics-forms";
|
|
10
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
11
|
+
import { ValidationError } from "../../../errors/validation-error";
|
|
9
12
|
const __P = {
|
|
10
13
|
p5: "p-[var(--appkit-space-5)]",
|
|
11
14
|
};
|
|
@@ -79,17 +82,29 @@ export function SellerAnalyticsAlertsView({ labels = {}, }) {
|
|
|
79
82
|
const alerts = data ?? [];
|
|
80
83
|
const createMutation = useApiMutation({
|
|
81
84
|
mutationFn: async () => {
|
|
82
|
-
|
|
85
|
+
/*
|
|
86
|
+
* Parsed against the SAME schema the route uses, before the request.
|
|
87
|
+
*
|
|
88
|
+
* This form used to hand-build its payload and included `scope: "seller"`
|
|
89
|
+
* — a field the route pins from the session and the schema does not
|
|
90
|
+
* declare. Once that schema became `.strict()`, every "Create Alert"
|
|
91
|
+
* became a 400, and nothing local could have caught it: the client had
|
|
92
|
+
* no schema to check against. Parsing here is the structural fix, not
|
|
93
|
+
* the deletion of one key.
|
|
94
|
+
*/
|
|
95
|
+
const parsed = analyticsAlertCreateSchema.safeParse({
|
|
83
96
|
label: draft.label,
|
|
84
97
|
metric: draft.metric,
|
|
85
98
|
operator: draft.operator,
|
|
86
|
-
threshold:
|
|
87
|
-
windowHours:
|
|
99
|
+
threshold: draft.threshold,
|
|
100
|
+
windowHours: draft.windowHours,
|
|
88
101
|
notifyChannels: draft.notifyChannels,
|
|
89
|
-
scope: "seller",
|
|
90
102
|
isActive: true,
|
|
91
|
-
};
|
|
92
|
-
|
|
103
|
+
});
|
|
104
|
+
if (!parsed.success) {
|
|
105
|
+
throw new ValidationError(parsed.error.issues[0]?.message ?? "Invalid alert", parsed.error.issues);
|
|
106
|
+
}
|
|
107
|
+
return apiClient.post(SELLER_ENDPOINTS.ANALYTICS_ALERTS, parsed.data);
|
|
93
108
|
},
|
|
94
109
|
onSuccess: () => {
|
|
95
110
|
showToast("Alert created", "success");
|
|
@@ -97,7 +112,7 @@ export function SellerAnalyticsAlertsView({ labels = {}, }) {
|
|
|
97
112
|
setShowForm(false);
|
|
98
113
|
setDraft(EMPTY_DRAFT);
|
|
99
114
|
},
|
|
100
|
-
onError: () => showToast("Failed to create alert", "error"),
|
|
115
|
+
onError: (err) => showToast(err?.message ?? "Failed to create alert", "error"),
|
|
101
116
|
});
|
|
102
117
|
const toggleMutation = useApiMutation({
|
|
103
118
|
mutationFn: async ({ id, isActive }) => apiClient.patch(SELLER_ENDPOINTS.ANALYTICS_ALERT_BY_ID(id), { isActive }),
|
|
@@ -122,7 +137,10 @@ export function SellerAnalyticsAlertsView({ labels = {}, }) {
|
|
|
122
137
|
: [...d.notifyChannels, ch],
|
|
123
138
|
}));
|
|
124
139
|
}, []);
|
|
125
|
-
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { justify: "between", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", color: "primary", children: labels.title ?? "Analytics Alerts" }), _jsx(Button, { size: "sm", onClick: () => setShowForm((v) => !v), children: showForm ? "Cancel" : "+ New Alert" })] }), _jsx(Text, { size: "sm", color: "muted", children: "Get notified when a key metric crosses a threshold. Alerts check the metric over the selected window and fire through your chosen channels." }), showForm && (_jsxs(Section, { className: `${__P.p5} space-y-[1rem]`, rounded: "xl", border: "default", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "primary", children: "Create Alert" }), _jsxs(Form, {
|
|
140
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { justify: "between", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", color: "primary", children: labels.title ?? "Analytics Alerts" }), _jsx(Button, { size: "sm", onClick: () => setShowForm((v) => !v), children: showForm ? "Cancel" : "+ New Alert" })] }), _jsx(Text, { size: "sm", color: "muted", children: "Get notified when a key metric crosses a threshold. Alerts check the metric over the selected window and fire through your chosen channels." }), showForm && (_jsxs(Section, { className: `${__P.p5} space-y-[1rem]`, rounded: "xl", border: "default", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "primary", children: "Create Alert" }), _jsxs(Form, { schema: analyticsAlertCreateSchema, onSubmit: (e) => {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
createMutation.mutate();
|
|
143
|
+
}, children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Alert label", value: draft.label, onChange: (e) => setDraft((d) => ({ ...d, label: e.target.value })), placeholder: "e.g. Low daily orders", required: true }), _jsx(Select, { label: "Metric", value: draft.metric, onValueChange: (v) => setDraft((d) => ({ ...d, metric: v })), options: METRIC_OPTIONS }), _jsx(Select, { label: "Operator", value: draft.operator, onValueChange: (v) => setDraft((d) => ({ ...d, operator: v })), options: OPERATOR_OPTIONS }), _jsx(Input, { label: "Threshold", type: "number", value: draft.threshold, onChange: (e) => setDraft((d) => ({ ...d, threshold: e.target.value })), placeholder: "e.g. 10", required: true }), _jsx(Select, { label: "Check window", value: draft.windowHours, onValueChange: (v) => setDraft((d) => ({ ...d, windowHours: v })), options: WINDOW_OPTIONS })] }), _jsxs(Div, { className: "mt-3", children: [_jsx(Text, { className: "mb-2", color: "muted", size: "xs", weight: "medium", children: "Notify via" }), _jsx(Row, { gap: "sm", wrap: true, children: CHANNEL_OPTIONS.map((ch) => (_jsx(Button, { variant: "outline", type: "button", onClick: () => toggleChannel(ch.value), rounded: "full", paddingX: "sm", paddingY: "xs", textSize: "xs", className: draft.notifyChannels.includes(ch.value)
|
|
126
144
|
? "bg-[var(--appkit-color-primary)] text-white border-transparent"
|
|
127
|
-
: "bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-muted)] border-[var(--appkit-color-border)]", children: ch.label }, ch.value))) })] }), _jsx(FormActions, { align: "right", className: "mt-4", children: _jsx(Button, { type: "submit", isLoading: createMutation.isPending, children: createMutation.isPending ? "Creating…" : "Create Alert" }) })] })] })), isLoading && (_jsx(Stack, { gap: "sm", children: [1, 2, 3].map((i) => (_jsx(Div, { className: "h-16 animate-pulse", surface: "subtle", rounded: "lg" }, i))) })), !isLoading && alerts.length === 0 && !showForm && (_jsx(Alert, { variant: "info", children: "No alerts configured. Create an alert to get notified when a key metric crosses a threshold." })), !isLoading && alerts.length > 0 && (_jsx(Stack, { gap: "3", children: alerts.map((alert) => (_jsx(AlertCard, { alert: alert, onToggle: (id, isActive) => toggleMutation.mutate({ id, isActive }), onDelete: (id) => deleteMutation.mutate(id) }, alert.id))) }))] }));
|
|
145
|
+
: "bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-muted)] border-[var(--appkit-color-border)]", children: ch.label }, ch.value))) })] }), _jsx(FormErrorSummary, {}), _jsx(FormActions, { align: "right", className: "mt-4", children: _jsx(Button, { type: "submit", isLoading: createMutation.isPending, children: createMutation.isPending ? "Creating…" : "Create Alert" }) })] })] })), isLoading && (_jsx(Stack, { gap: "sm", children: [1, 2, 3].map((i) => (_jsx(Div, { className: "h-16 animate-pulse", surface: "subtle", rounded: "lg" }, i))) })), !isLoading && alerts.length === 0 && !showForm && (_jsx(Alert, { variant: "info", children: "No alerts configured. Create an alert to get notified when a key metric crosses a threshold." })), !isLoading && alerts.length > 0 && (_jsx(Stack, { gap: "3", children: alerts.map((alert) => (_jsx(AlertCard, { alert: alert, onToggle: (id, isActive) => toggleMutation.mutate({ id, isActive }), onDelete: (id) => deleteMutation.mutate(id) }, alert.id))) }))] }));
|
|
128
146
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const LISTING_TEMPLATE_TYPE_OPTIONS: {
|
|
3
|
+
value: "art" | "stickers" | "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | "bundle";
|
|
4
|
+
label: string;
|
|
5
|
+
}[];
|
|
2
6
|
/**
|
|
3
7
|
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
4
8
|
* by schema instance and every zod wrapper returns a new one.
|
|
@@ -54,6 +58,20 @@ export declare const listingTemplateCreateSchema: z.ZodObject<{
|
|
|
54
58
|
defaults?: Record<string, import("../../..").FirestoreValue> | undefined;
|
|
55
59
|
}>;
|
|
56
60
|
/** Update contract — every field optional, and the same ownership fields absent. */
|
|
61
|
+
/**
|
|
62
|
+
* Update contract.
|
|
63
|
+
*
|
|
64
|
+
* `.strict()` as well as `.partial()`: the edit page used to PATCH the whole
|
|
65
|
+
* loaded document — `id`, `storeId`, `createdAt`, `updatedAt`, `usageCount`
|
|
66
|
+
* and all — and a non-strict schema silently stripped them, which reads as a
|
|
67
|
+
* clean save while quietly discarding half the payload. The page now sends
|
|
68
|
+
* only the editable fields, so a stray key here is a real mistake and should
|
|
69
|
+
* be a 400.
|
|
70
|
+
*
|
|
71
|
+
* `listingType` remains accepted-but-optional rather than removed: it is not
|
|
72
|
+
* editable in the UI (changing a template's type would invalidate every
|
|
73
|
+
* default stored against it), but the create path shares this shape.
|
|
74
|
+
*/
|
|
57
75
|
export declare const listingTemplateUpdateSchema: z.ZodObject<{
|
|
58
76
|
name: z.ZodOptional<z.ZodString>;
|
|
59
77
|
description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
@@ -61,7 +79,7 @@ export declare const listingTemplateUpdateSchema: z.ZodObject<{
|
|
|
61
79
|
defaults: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("../../..").FirestoreValue, z.ZodTypeDef, import("../../..").FirestoreValue>>>;
|
|
62
80
|
isShared: z.ZodOptional<z.ZodBoolean>;
|
|
63
81
|
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
64
|
-
}, "
|
|
82
|
+
}, "strict", z.ZodTypeAny, {
|
|
65
83
|
name?: string | undefined;
|
|
66
84
|
listingType?: "art" | "stickers" | "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | "bundle" | undefined;
|
|
67
85
|
description?: string | undefined;
|
|
@@ -41,6 +41,33 @@ import { firestoreValueSchema } from "../../../schemas/firestore-value";
|
|
|
41
41
|
* templates while `bundle` lingered.
|
|
42
42
|
*/
|
|
43
43
|
const listingTemplateTypeSchema = z.enum(LISTING_TEMPLATE_TYPES);
|
|
44
|
+
/**
|
|
45
|
+
* Dropdown options, derived from the same runtime array the schema validates
|
|
46
|
+
* against.
|
|
47
|
+
*
|
|
48
|
+
* 🛑 The New Template page carried its own hand-written list of EIGHT of these
|
|
49
|
+
* ten. `art` and `stickers` were added to the union but not to that literal,
|
|
50
|
+
* so a seller could not create a template for either — the same defect the
|
|
51
|
+
* homepage-section create route had (21 values against a union of 25), one
|
|
52
|
+
* domain over. Labels are keyed `Record<ListingTemplateType, string>`, so a
|
|
53
|
+
* new type is a compile error here rather than a missing dropdown entry.
|
|
54
|
+
*/
|
|
55
|
+
const LISTING_TEMPLATE_TYPE_LABEL = {
|
|
56
|
+
standard: "Standard",
|
|
57
|
+
auction: "Auction",
|
|
58
|
+
"pre-order": "Pre-order",
|
|
59
|
+
"prize-draw": "Prize draw",
|
|
60
|
+
bundle: "Bundle",
|
|
61
|
+
classified: "Classified",
|
|
62
|
+
"digital-code": "Digital code",
|
|
63
|
+
live: "Live item",
|
|
64
|
+
art: "Art print",
|
|
65
|
+
stickers: "Sticker sheet",
|
|
66
|
+
};
|
|
67
|
+
export const LISTING_TEMPLATE_TYPE_OPTIONS = LISTING_TEMPLATE_TYPES.map((value) => ({
|
|
68
|
+
value,
|
|
69
|
+
label: LISTING_TEMPLATE_TYPE_LABEL[value],
|
|
70
|
+
}));
|
|
44
71
|
/**
|
|
45
72
|
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
46
73
|
* by schema instance and every zod wrapper returns a new one.
|
|
@@ -84,4 +111,18 @@ export const listingTemplateCreateSchema = listingTemplateFormSchema.partial({
|
|
|
84
111
|
defaults: true,
|
|
85
112
|
});
|
|
86
113
|
/** Update contract — every field optional, and the same ownership fields absent. */
|
|
87
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Update contract.
|
|
116
|
+
*
|
|
117
|
+
* `.strict()` as well as `.partial()`: the edit page used to PATCH the whole
|
|
118
|
+
* loaded document — `id`, `storeId`, `createdAt`, `updatedAt`, `usageCount`
|
|
119
|
+
* and all — and a non-strict schema silently stripped them, which reads as a
|
|
120
|
+
* clean save while quietly discarding half the payload. The page now sends
|
|
121
|
+
* only the editable fields, so a stray key here is a real mistake and should
|
|
122
|
+
* be a 400.
|
|
123
|
+
*
|
|
124
|
+
* `listingType` remains accepted-but-optional rather than removed: it is not
|
|
125
|
+
* editable in the UI (changing a template's type would invalidate every
|
|
126
|
+
* default stored against it), but the create path shares this shape.
|
|
127
|
+
*/
|
|
128
|
+
export const listingTemplateUpdateSchema = listingTemplateFormSchema.partial().strict();
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type { ShippingMethod } from "./firestore";
|
|
3
|
+
/** Dropdown options for any surface that picks a shipping method. */
|
|
4
|
+
export declare const SHIPPING_METHOD_OPTIONS: {
|
|
5
|
+
value: ShippingMethod;
|
|
6
|
+
label: string;
|
|
7
|
+
}[];
|
|
2
8
|
/**
|
|
3
9
|
* 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
|
|
4
10
|
* by schema instance and every zod wrapper returns a new one.
|
|
5
11
|
*/
|
|
6
12
|
export declare const shippingConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
7
13
|
label: z.ZodString;
|
|
8
|
-
method: z.ZodEnum<[
|
|
14
|
+
method: z.ZodEnum<[ShippingMethod, ...ShippingMethod[]]>;
|
|
9
15
|
flatRate: z.ZodOptional<z.ZodNumber>;
|
|
10
16
|
pricePerKg: z.ZodOptional<z.ZodNumber>;
|
|
11
17
|
freeAbove: z.ZodOptional<z.ZodNumber>;
|
|
@@ -17,7 +23,7 @@ export declare const shippingConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
17
23
|
label: string;
|
|
18
24
|
isDefault: boolean;
|
|
19
25
|
isActive: boolean;
|
|
20
|
-
method:
|
|
26
|
+
method: ShippingMethod;
|
|
21
27
|
estimatedDays?: number | undefined;
|
|
22
28
|
freeAbove?: number | undefined;
|
|
23
29
|
flatRate?: number | undefined;
|
|
@@ -27,7 +33,7 @@ export declare const shippingConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
27
33
|
label: string;
|
|
28
34
|
isDefault: boolean;
|
|
29
35
|
isActive: boolean;
|
|
30
|
-
method:
|
|
36
|
+
method: ShippingMethod;
|
|
31
37
|
estimatedDays?: number | undefined;
|
|
32
38
|
freeAbove?: number | undefined;
|
|
33
39
|
flatRate?: number | undefined;
|
|
@@ -37,7 +43,7 @@ export declare const shippingConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
37
43
|
label: string;
|
|
38
44
|
isDefault: boolean;
|
|
39
45
|
isActive: boolean;
|
|
40
|
-
method:
|
|
46
|
+
method: ShippingMethod;
|
|
41
47
|
estimatedDays?: number | undefined;
|
|
42
48
|
freeAbove?: number | undefined;
|
|
43
49
|
flatRate?: number | undefined;
|
|
@@ -47,7 +53,7 @@ export declare const shippingConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
47
53
|
label: string;
|
|
48
54
|
isDefault: boolean;
|
|
49
55
|
isActive: boolean;
|
|
50
|
-
method:
|
|
56
|
+
method: ShippingMethod;
|
|
51
57
|
estimatedDays?: number | undefined;
|
|
52
58
|
freeAbove?: number | undefined;
|
|
53
59
|
flatRate?: number | undefined;
|
|
@@ -58,7 +64,7 @@ export type ShippingConfigFormValues = z.infer<typeof shippingConfigFormSchema>;
|
|
|
58
64
|
/** `storeId` is absent — the route sets it from the session, never the body. */
|
|
59
65
|
export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
60
66
|
label: z.ZodString;
|
|
61
|
-
method: z.ZodEnum<[
|
|
67
|
+
method: z.ZodEnum<[ShippingMethod, ...ShippingMethod[]]>;
|
|
62
68
|
flatRate: z.ZodOptional<z.ZodNumber>;
|
|
63
69
|
pricePerKg: z.ZodOptional<z.ZodNumber>;
|
|
64
70
|
freeAbove: z.ZodOptional<z.ZodNumber>;
|
|
@@ -70,7 +76,7 @@ export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
|
70
76
|
label: string;
|
|
71
77
|
isDefault: boolean;
|
|
72
78
|
isActive: boolean;
|
|
73
|
-
method:
|
|
79
|
+
method: ShippingMethod;
|
|
74
80
|
estimatedDays?: number | undefined;
|
|
75
81
|
freeAbove?: number | undefined;
|
|
76
82
|
flatRate?: number | undefined;
|
|
@@ -80,7 +86,7 @@ export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
|
80
86
|
label: string;
|
|
81
87
|
isDefault: boolean;
|
|
82
88
|
isActive: boolean;
|
|
83
|
-
method:
|
|
89
|
+
method: ShippingMethod;
|
|
84
90
|
estimatedDays?: number | undefined;
|
|
85
91
|
freeAbove?: number | undefined;
|
|
86
92
|
flatRate?: number | undefined;
|
|
@@ -90,7 +96,7 @@ export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
|
90
96
|
label: string;
|
|
91
97
|
isDefault: boolean;
|
|
92
98
|
isActive: boolean;
|
|
93
|
-
method:
|
|
99
|
+
method: ShippingMethod;
|
|
94
100
|
estimatedDays?: number | undefined;
|
|
95
101
|
freeAbove?: number | undefined;
|
|
96
102
|
flatRate?: number | undefined;
|
|
@@ -100,7 +106,7 @@ export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
|
100
106
|
label: string;
|
|
101
107
|
isDefault: boolean;
|
|
102
108
|
isActive: boolean;
|
|
103
|
-
method:
|
|
109
|
+
method: ShippingMethod;
|
|
104
110
|
estimatedDays?: number | undefined;
|
|
105
111
|
freeAbove?: number | undefined;
|
|
106
112
|
flatRate?: number | undefined;
|
|
@@ -114,7 +120,7 @@ export declare const shippingConfigCreateSchema: z.ZodEffects<z.ZodObject<{
|
|
|
114
120
|
*/
|
|
115
121
|
export declare const shippingConfigUpdateSchema: z.ZodObject<{
|
|
116
122
|
label: z.ZodOptional<z.ZodString>;
|
|
117
|
-
method: z.ZodOptional<z.ZodEnum<[
|
|
123
|
+
method: z.ZodOptional<z.ZodEnum<[ShippingMethod, ...ShippingMethod[]]>>;
|
|
118
124
|
flatRate: z.ZodOptional<z.ZodNumber>;
|
|
119
125
|
pricePerKg: z.ZodOptional<z.ZodNumber>;
|
|
120
126
|
freeAbove: z.ZodOptional<z.ZodNumber>;
|
|
@@ -127,7 +133,7 @@ export declare const shippingConfigUpdateSchema: z.ZodObject<{
|
|
|
127
133
|
label?: string | undefined;
|
|
128
134
|
isDefault?: boolean | undefined;
|
|
129
135
|
isActive?: boolean | undefined;
|
|
130
|
-
method?:
|
|
136
|
+
method?: ShippingMethod | undefined;
|
|
131
137
|
estimatedDays?: number | undefined;
|
|
132
138
|
freeAbove?: number | undefined;
|
|
133
139
|
flatRate?: number | undefined;
|
|
@@ -138,7 +144,7 @@ export declare const shippingConfigUpdateSchema: z.ZodObject<{
|
|
|
138
144
|
label?: string | undefined;
|
|
139
145
|
isDefault?: boolean | undefined;
|
|
140
146
|
isActive?: boolean | undefined;
|
|
141
|
-
method?:
|
|
147
|
+
method?: ShippingMethod | undefined;
|
|
142
148
|
estimatedDays?: number | undefined;
|
|
143
149
|
freeAbove?: number | undefined;
|
|
144
150
|
flatRate?: number | undefined;
|
|
@@ -26,8 +26,31 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { z } from "zod";
|
|
28
28
|
import { annotate } from "../../shell/field-ui-meta";
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/*
|
|
30
|
+
* Method labels, keyed on the real union.
|
|
31
|
+
*
|
|
32
|
+
* This was a hand-written `z.enum([...])` with a comment saying it "mirrors
|
|
33
|
+
* ShippingMethod" — the shape that always eventually stops mirroring. Keyed
|
|
34
|
+
* `Record<ShippingMethod, string>`, a value added to the union is now a
|
|
35
|
+
* compile error here, and the enum and the dropdown options both derive from
|
|
36
|
+
* this one map rather than from two separate literals (the edit page carried
|
|
37
|
+
* its own copy of the six options inline).
|
|
38
|
+
*/
|
|
39
|
+
const SHIPPING_METHOD_LABEL = {
|
|
40
|
+
free: "Free",
|
|
41
|
+
flat: "Flat rate",
|
|
42
|
+
weight: "By weight",
|
|
43
|
+
express: "Express",
|
|
44
|
+
pickup: "Pickup",
|
|
45
|
+
custom: "Custom",
|
|
46
|
+
};
|
|
47
|
+
const SHIPPING_METHODS = Object.keys(SHIPPING_METHOD_LABEL);
|
|
48
|
+
const shippingMethodSchema = z.enum(SHIPPING_METHODS);
|
|
49
|
+
/** Dropdown options for any surface that picks a shipping method. */
|
|
50
|
+
export const SHIPPING_METHOD_OPTIONS = SHIPPING_METHODS.map((value) => ({
|
|
51
|
+
value,
|
|
52
|
+
label: SHIPPING_METHOD_LABEL[value],
|
|
53
|
+
}));
|
|
31
54
|
/** A non-negative money/threshold amount, coerced from the form's string. */
|
|
32
55
|
const amount = (label) => z.coerce.number({ invalid_type_error: `${label} must be a number.` }).min(0, `${label} cannot be negative.`);
|
|
33
56
|
/**
|
|
@@ -10,17 +10,20 @@ export declare const storeCategoryFormSchema: z.ZodObject<{
|
|
|
10
10
|
description: z.ZodOptional<z.ZodString>;
|
|
11
11
|
coverImageUrl: z.ZodOptional<z.ZodString>;
|
|
12
12
|
displayOrder: z.ZodNumber;
|
|
13
|
+
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
13
14
|
}, "strip", z.ZodTypeAny, {
|
|
14
15
|
label: string;
|
|
15
16
|
displayOrder: number;
|
|
16
17
|
slug?: string | undefined;
|
|
17
18
|
description?: string | undefined;
|
|
19
|
+
isActive?: boolean | undefined;
|
|
18
20
|
coverImageUrl?: string | undefined;
|
|
19
21
|
}, {
|
|
20
22
|
label: string;
|
|
21
23
|
displayOrder: number;
|
|
22
24
|
slug?: string | undefined;
|
|
23
25
|
description?: string | undefined;
|
|
26
|
+
isActive?: boolean | undefined;
|
|
24
27
|
coverImageUrl?: string | undefined;
|
|
25
28
|
}>;
|
|
26
29
|
export type StoreCategoryFormValues = z.infer<typeof storeCategoryFormSchema>;
|
|
@@ -37,6 +40,7 @@ export declare const storeCategoryCreateSchema: z.ZodObject<{
|
|
|
37
40
|
description: z.ZodOptional<z.ZodString>;
|
|
38
41
|
coverImageUrl: z.ZodOptional<z.ZodString>;
|
|
39
42
|
displayOrder: z.ZodNumber;
|
|
43
|
+
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
40
44
|
} & {
|
|
41
45
|
slug: z.ZodString;
|
|
42
46
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -44,11 +48,47 @@ export declare const storeCategoryCreateSchema: z.ZodObject<{
|
|
|
44
48
|
slug: string;
|
|
45
49
|
displayOrder: number;
|
|
46
50
|
description?: string | undefined;
|
|
51
|
+
isActive?: boolean | undefined;
|
|
47
52
|
coverImageUrl?: string | undefined;
|
|
48
53
|
}, {
|
|
49
54
|
label: string;
|
|
50
55
|
slug: string;
|
|
51
56
|
displayOrder: number;
|
|
52
57
|
description?: string | undefined;
|
|
58
|
+
isActive?: boolean | undefined;
|
|
59
|
+
coverImageUrl?: string | undefined;
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* Update contract.
|
|
63
|
+
*
|
|
64
|
+
* `.partial()` so a PATCH may carry one field, and `.strict()` so an unknown
|
|
65
|
+
* key is a 400 rather than a silent write. `slug` is deliberately still
|
|
66
|
+
* accepted here, unlike on categories/brands/bundles: a STOREFRONT category is
|
|
67
|
+
* seller-local organisation with no public permalink of its own, so renaming
|
|
68
|
+
* one breaks no inbound link.
|
|
69
|
+
*
|
|
70
|
+
* `storeId` is absent — the route sets it from the session, so a seller cannot
|
|
71
|
+
* move a category into another store.
|
|
72
|
+
*/
|
|
73
|
+
export declare const storeCategoryUpdateSchema: z.ZodObject<{
|
|
74
|
+
label: z.ZodOptional<z.ZodString>;
|
|
75
|
+
slug: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
76
|
+
description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
77
|
+
coverImageUrl: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
78
|
+
displayOrder: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
isActive: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
80
|
+
}, "strict", z.ZodTypeAny, {
|
|
81
|
+
label?: string | undefined;
|
|
82
|
+
slug?: string | undefined;
|
|
83
|
+
description?: string | undefined;
|
|
84
|
+
isActive?: boolean | undefined;
|
|
85
|
+
displayOrder?: number | undefined;
|
|
86
|
+
coverImageUrl?: string | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
label?: string | undefined;
|
|
89
|
+
slug?: string | undefined;
|
|
90
|
+
description?: string | undefined;
|
|
91
|
+
isActive?: boolean | undefined;
|
|
92
|
+
displayOrder?: number | undefined;
|
|
53
93
|
coverImageUrl?: string | undefined;
|
|
54
94
|
}>;
|
|
@@ -59,6 +59,21 @@ export const storeCategoryFormSchema = z.object({
|
|
|
59
59
|
section: "media", sectionLabel: "Media", order: 1, row: "full", kind: "media",
|
|
60
60
|
}),
|
|
61
61
|
displayOrder: annotate(z.coerce.number().int("Display order must be a whole number.").min(0, "Display order cannot be negative."), { section: "visibility", sectionLabel: "Visibility", order: 1, row: "pair" }),
|
|
62
|
+
/*
|
|
63
|
+
* 🛑 This field was MISSING, and `StoreCategoryDocument.isActive` is
|
|
64
|
+
* required.
|
|
65
|
+
*
|
|
66
|
+
* The edit page has always rendered an "Active" toggle and sent its value in
|
|
67
|
+
* the body; the schema simply did not name it. It surfaced the moment that
|
|
68
|
+
* page was typed against this schema — which is the argument for wiring a
|
|
69
|
+
* schema to its form rather than only to its route.
|
|
70
|
+
*
|
|
71
|
+
* Optional here (not `.min`) because a PATCH may legitimately omit it; the
|
|
72
|
+
* document default is `true`.
|
|
73
|
+
*/
|
|
74
|
+
isActive: annotate(z.boolean().optional(), {
|
|
75
|
+
section: "visibility", order: 2, row: "quarter",
|
|
76
|
+
}),
|
|
62
77
|
});
|
|
63
78
|
/**
|
|
64
79
|
* The server's create contract.
|
|
@@ -71,3 +86,16 @@ export const storeCategoryFormSchema = z.object({
|
|
|
71
86
|
export const storeCategoryCreateSchema = storeCategoryFormSchema.extend({
|
|
72
87
|
slug: z.string().min(1, "Slug is required").max(80),
|
|
73
88
|
});
|
|
89
|
+
/**
|
|
90
|
+
* Update contract.
|
|
91
|
+
*
|
|
92
|
+
* `.partial()` so a PATCH may carry one field, and `.strict()` so an unknown
|
|
93
|
+
* key is a 400 rather than a silent write. `slug` is deliberately still
|
|
94
|
+
* accepted here, unlike on categories/brands/bundles: a STOREFRONT category is
|
|
95
|
+
* seller-local organisation with no public permalink of its own, so renaming
|
|
96
|
+
* one breaks no inbound link.
|
|
97
|
+
*
|
|
98
|
+
* `storeId` is absent — the route sets it from the session, so a seller cannot
|
|
99
|
+
* move a category into another store.
|
|
100
|
+
*/
|
|
101
|
+
export const storeCategoryUpdateSchema = storeCategoryFormSchema.partial().strict();
|
package/dist/index.d.ts
CHANGED
|
@@ -493,7 +493,7 @@ export type { ResolvedUser } from "./security/index";
|
|
|
493
493
|
export type { RoleDefinition } from "./security/index";
|
|
494
494
|
export type { UserRole } from "./security/index";
|
|
495
495
|
export { AddressesRepository } from "./repositories/index";
|
|
496
|
-
export { type PayoutMethodType, type PayoutMethodDocument, PAYOUT_METHODS_COLLECTION, PAYOUT_METHOD_INDEXED_FIELDS, DEFAULT_PAYOUT_METHOD_DATA, type ShippingMethod as ShippingConfigMethod, type ShippingConfigDocument, SHIPPING_CONFIGS_COLLECTION, SHIPPING_CONFIG_INDEXED_FIELDS, DEFAULT_SHIPPING_CONFIG_DATA, type AnalyticsScope, type AnalyticsCardType, type AnalyticsCardDocument, ANALYTICS_CARDS_COLLECTION, ANALYTICS_CARD_INDEXED_FIELDS, DEFAULT_ANALYTICS_CARD_DATA, type AlertOperator, type AnalyticsAlertDocument, ANALYTICS_ALERTS_COLLECTION, ANALYTICS_ALERT_INDEXED_FIELDS, DEFAULT_ANALYTICS_ALERT_DATA, type StoreCategoryDocument, storeCategoryFormSchema, storeCategoryCreateSchema, type StoreCategoryFormValues, listingTemplateFormSchema, listingTemplateCreateSchema, listingTemplateUpdateSchema, type ListingTemplateFormValues, LISTING_TEMPLATE_TYPES, payoutMethodFormSchema, payoutMethodCreateSchema, payoutMethodUpdateSchema, type PayoutMethodFormValues, shippingConfigFormSchema, shippingConfigCreateSchema, shippingConfigUpdateSchema, type ShippingConfigFormValues, customRoleFormSchema, customRoleCreateSchema, customRoleUpdateSchema, isKnownPermission, type CustomRoleFormValues, moderationReviewFormSchema, moderationReviewUpdateSchema, MODERATION_REVIEWER_STATUSES, type ModerationReviewFormValues, reportReviewFormSchema, reportReviewUpdateSchema, REPORT_REVIEWER_STATUSES, REPORT_TERMINAL_STATUSES, type ReportReviewFormValues, reportCreateSchema, type ReportCreateValues, itemRequestCreateSchema, type ItemRequestCreateValues, REPORT_ENTITY_TYPES, REPORT_REASONS, analyticsAlertCreateSchema, type AnalyticsAlertCreateValues, analyticsCardCreateSchema, type AnalyticsCardCreateValues, ANALYTICS_ALERT_OPERATORS, ANALYTICS_CARD_TYPES, storeGoogleConfigUpdateSchema, type StoreGoogleConfigFormValues, toSellerGoogleConfig, type SellerGoogleConfig, adminNotificationCreateSchema, type AdminNotificationCreateValues, ADMIN_NOTIFICATION_CATEGORIES, ADMIN_NOTIFICATION_SEVERITIES, STORE_CATEGORIES_COLLECTION, STORE_CATEGORY_INDEXED_FIELDS, DEFAULT_STORE_CATEGORY_DATA, type ListingTemplateType, type ListingTemplateDocument, LISTING_TEMPLATES_COLLECTION, LISTING_TEMPLATE_INDEXED_FIELDS, DEFAULT_LISTING_TEMPLATE_DATA, type ModerationStatus, type ModerationMediaType, type ModerationQueueDocument, MODERATION_QUEUE_COLLECTION, MODERATION_QUEUE_INDEXED_FIELDS, DEFAULT_MODERATION_QUEUE_DATA, type ReportEntityType, type ReportStatus, type ReportReason, type ReportDocument, REPORTS_COLLECTION, REPORT_INDEXED_FIELDS, DEFAULT_REPORT_DATA, type AdminNotificationCategory, type AdminNotificationDocument, ADMIN_NOTIFICATIONS_COLLECTION, ADMIN_NOTIFICATION_INDEXED_FIELDS, DEFAULT_ADMIN_NOTIFICATION_DATA, type ItemRequestStatus, type ItemRequestReply, type ItemRequestDocument, ITEM_REQUESTS_COLLECTION, ITEM_REQUEST_INDEXED_FIELDS, DEFAULT_ITEM_REQUEST_DATA, type StoreGoogleConfigDocument, STORE_GOOGLE_CONFIG_COLLECTION, STORE_GOOGLE_CONFIG_INDEXED_FIELDS, DEFAULT_STORE_GOOGLE_CONFIG_DATA, type StoreWhatsAppConfigDocument, STORE_WHATSAPP_CONFIG_COLLECTION, STORE_WHATSAPP_CONFIG_INDEXED_FIELDS, DEFAULT_STORE_WHATSAPP_CONFIG_DATA, type CustomRoleDocument, CUSTOM_ROLES_COLLECTION, CUSTOM_ROLE_INDEXED_FIELDS, DEFAULT_CUSTOM_ROLE_DATA, type RoleOverrideDocument, ROLE_OVERRIDES_COLLECTION, ROLE_OVERRIDE_INDEXED_FIELDS, DEFAULT_ROLE_OVERRIDE_DATA, PayoutMethodsRepository, ShippingConfigsRepository, AnalyticsCardsRepository, AnalyticsAlertsRepository, StoreCategoriesRepository, ListingTemplatesRepository, ModerationQueueRepository, ReportsRepository, AdminNotificationsRepository, ItemRequestsRepository, StoreGoogleConfigRepository, StoreWhatsAppConfigRepository, CustomRolesRepository, RoleOverridesRepository, payoutMethodsRepository, shippingConfigsRepository, analyticsCardsRepository, analyticsAlertsRepository, storeCategoriesRepository, listingTemplatesRepository, moderationQueueRepository, reportsRepository, adminNotificationsRepository, itemRequestsRepository, storeGoogleConfigRepository, storeWhatsAppConfigRepository, customRolesRepository, roleOverridesRepository, } from "./features/store-extensions/index";
|
|
496
|
+
export { type PayoutMethodType, type PayoutMethodDocument, PAYOUT_METHODS_COLLECTION, PAYOUT_METHOD_INDEXED_FIELDS, DEFAULT_PAYOUT_METHOD_DATA, type ShippingMethod as ShippingConfigMethod, type ShippingConfigDocument, SHIPPING_CONFIGS_COLLECTION, SHIPPING_CONFIG_INDEXED_FIELDS, DEFAULT_SHIPPING_CONFIG_DATA, type AnalyticsScope, type AnalyticsCardType, type AnalyticsCardDocument, ANALYTICS_CARDS_COLLECTION, ANALYTICS_CARD_INDEXED_FIELDS, DEFAULT_ANALYTICS_CARD_DATA, type AlertOperator, type AnalyticsAlertDocument, ANALYTICS_ALERTS_COLLECTION, ANALYTICS_ALERT_INDEXED_FIELDS, DEFAULT_ANALYTICS_ALERT_DATA, type StoreCategoryDocument, storeCategoryFormSchema, storeCategoryCreateSchema, storeCategoryUpdateSchema, type StoreCategoryFormValues, listingTemplateFormSchema, listingTemplateCreateSchema, listingTemplateUpdateSchema, type ListingTemplateFormValues, LISTING_TEMPLATE_TYPES, LISTING_TEMPLATE_TYPE_OPTIONS, payoutMethodFormSchema, payoutMethodCreateSchema, payoutMethodUpdateSchema, type PayoutMethodFormValues, shippingConfigFormSchema, SHIPPING_METHOD_OPTIONS, shippingConfigCreateSchema, shippingConfigUpdateSchema, type ShippingConfigFormValues, customRoleFormSchema, customRoleCreateSchema, customRoleUpdateSchema, isKnownPermission, type CustomRoleFormValues, moderationReviewFormSchema, moderationReviewUpdateSchema, MODERATION_REVIEWER_STATUSES, type ModerationReviewFormValues, reportReviewFormSchema, reportReviewUpdateSchema, REPORT_REVIEWER_STATUSES, REPORT_TERMINAL_STATUSES, type ReportReviewFormValues, reportCreateSchema, type ReportCreateValues, itemRequestCreateSchema, type ItemRequestCreateValues, REPORT_ENTITY_TYPES, REPORT_REASONS, analyticsAlertCreateSchema, type AnalyticsAlertCreateValues, analyticsCardCreateSchema, type AnalyticsCardCreateValues, ANALYTICS_ALERT_OPERATORS, ANALYTICS_CARD_TYPES, storeGoogleConfigUpdateSchema, type StoreGoogleConfigFormValues, toSellerGoogleConfig, type SellerGoogleConfig, adminNotificationCreateSchema, type AdminNotificationCreateValues, ADMIN_NOTIFICATION_CATEGORIES, ADMIN_NOTIFICATION_SEVERITIES, STORE_CATEGORIES_COLLECTION, STORE_CATEGORY_INDEXED_FIELDS, DEFAULT_STORE_CATEGORY_DATA, type ListingTemplateType, type ListingTemplateDocument, LISTING_TEMPLATES_COLLECTION, LISTING_TEMPLATE_INDEXED_FIELDS, DEFAULT_LISTING_TEMPLATE_DATA, type ModerationStatus, type ModerationMediaType, type ModerationQueueDocument, MODERATION_QUEUE_COLLECTION, MODERATION_QUEUE_INDEXED_FIELDS, DEFAULT_MODERATION_QUEUE_DATA, type ReportEntityType, type ReportStatus, type ReportReason, type ReportDocument, REPORTS_COLLECTION, REPORT_INDEXED_FIELDS, DEFAULT_REPORT_DATA, type AdminNotificationCategory, type AdminNotificationDocument, ADMIN_NOTIFICATIONS_COLLECTION, ADMIN_NOTIFICATION_INDEXED_FIELDS, DEFAULT_ADMIN_NOTIFICATION_DATA, type ItemRequestStatus, type ItemRequestReply, type ItemRequestDocument, ITEM_REQUESTS_COLLECTION, ITEM_REQUEST_INDEXED_FIELDS, DEFAULT_ITEM_REQUEST_DATA, type StoreGoogleConfigDocument, STORE_GOOGLE_CONFIG_COLLECTION, STORE_GOOGLE_CONFIG_INDEXED_FIELDS, DEFAULT_STORE_GOOGLE_CONFIG_DATA, type StoreWhatsAppConfigDocument, STORE_WHATSAPP_CONFIG_COLLECTION, STORE_WHATSAPP_CONFIG_INDEXED_FIELDS, DEFAULT_STORE_WHATSAPP_CONFIG_DATA, type CustomRoleDocument, CUSTOM_ROLES_COLLECTION, CUSTOM_ROLE_INDEXED_FIELDS, DEFAULT_CUSTOM_ROLE_DATA, type RoleOverrideDocument, ROLE_OVERRIDES_COLLECTION, ROLE_OVERRIDE_INDEXED_FIELDS, DEFAULT_ROLE_OVERRIDE_DATA, PayoutMethodsRepository, ShippingConfigsRepository, AnalyticsCardsRepository, AnalyticsAlertsRepository, StoreCategoriesRepository, ListingTemplatesRepository, ModerationQueueRepository, ReportsRepository, AdminNotificationsRepository, ItemRequestsRepository, StoreGoogleConfigRepository, StoreWhatsAppConfigRepository, CustomRolesRepository, RoleOverridesRepository, payoutMethodsRepository, shippingConfigsRepository, analyticsCardsRepository, analyticsAlertsRepository, storeCategoriesRepository, listingTemplatesRepository, moderationQueueRepository, reportsRepository, adminNotificationsRepository, itemRequestsRepository, storeGoogleConfigRepository, storeWhatsAppConfigRepository, customRolesRepository, roleOverridesRepository, } from "./features/store-extensions/index";
|
|
497
497
|
export { BaseRepository } from "./repositories/index";
|
|
498
498
|
export { BlogRepository } from "./repositories/index";
|
|
499
499
|
export { CartRepository } from "./repositories/index";
|
|
@@ -3284,3 +3284,5 @@ export { withRetry } from "./http/retry";
|
|
|
3284
3284
|
export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFirebaseAuthNormalized, isFirebaseFirestoreNormalized, isFirebaseStorageNormalized, isNativeNormalized, isNetworkNormalized, isUnknownNormalized, isZodNormalized, } from "./errors/normalize";
|
|
3285
3285
|
export type { NormalizedError, NormalizedApiError, NormalizedAppError, NormalizedFirebaseAuthError, NormalizedFirebaseFirestoreError, NormalizedFirebaseStorageError, NormalizedNativeError, NormalizedNetworkError, NormalizedUnknownThrownValue, NormalizedZodError, } from "./errors/normalize";
|
|
3286
3286
|
export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, type CartLane, type LaneAssignable, } from "./_internal/shared/checkout/lanes";
|
|
3287
|
+
export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
|
|
3288
|
+
export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
|