@mohasinac/appkit 4.17.0 → 4.19.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/client/features/lottery/LotteryAdminEditView.js +3 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +3 -1
- package/dist/_internal/shared/features/lottery/config-form.d.ts +58 -0
- package/dist/_internal/shared/features/lottery/config-form.js +71 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.js +8 -0
- package/dist/features/account/schemas/index.d.ts +23 -0
- package/dist/features/account/schemas/index.js +21 -0
- package/dist/features/admin/components/AdminAdEditorView.js +4 -2
- package/dist/features/admin/components/AdminAddressEditorView.js +4 -2
- package/dist/features/admin/components/AdminEmployeeEditorView.js +3 -1
- package/dist/features/admin/components/AdminFeatureFlagsView.js +4 -2
- package/dist/features/admin/components/AdminOrderEditorView.js +4 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +8 -6
- package/dist/features/admin/components/AdminSiteView.js +4 -2
- package/dist/features/admin/components/AdminStoreEditorView.js +11 -9
- package/dist/features/admin/components/AdminUserEditorView.js +14 -12
- package/dist/features/admin/components/AdminUsersView.js +4 -2
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +119 -0
- package/dist/features/admin/schemas/admin-editor-forms.js +141 -0
- package/dist/features/admin/schemas/admin-ops-forms.d.ts +123 -0
- package/dist/features/admin/schemas/admin-ops-forms.js +147 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +82 -0
- package/dist/features/admin/schemas/admin-user-form.js +102 -0
- package/dist/features/admin/schemas/site-settings-form.d.ts +309 -0
- package/dist/features/admin/schemas/site-settings-form.js +159 -0
- package/dist/features/auctions/components/PlaceBidFormClient.js +3 -2
- package/dist/features/orders/schemas/buyer-forms.d.ts +46 -0
- package/dist/features/orders/schemas/buyer-forms.js +81 -0
- package/dist/features/scams/schemas/report-form.d.ts +63 -0
- package/dist/features/scams/schemas/report-form.js +88 -0
- package/dist/features/store-extensions/schemas/sublisting-category-form.d.ts +15 -0
- package/dist/features/store-extensions/schemas/sublisting-category-form.js +41 -0
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +4 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/schemas/registry.d.ts +682 -0
- package/dist/schemas/registry.js +22 -0
- package/package.json +1 -1
|
@@ -4,6 +4,8 @@ import { useState, useTransition } from "react";
|
|
|
4
4
|
import { normalizeError } from "../../../../errors/normalize";
|
|
5
5
|
import { Stack, Row, Text, Heading, Section, Container, Button, DataTable, } from "../../../../ui";
|
|
6
6
|
import { Form, FieldInput, FieldSelect, Input } from "../../../../ui";
|
|
7
|
+
import { lotteryConfigFormSchema } from "../../../shared/features/lottery/config-form";
|
|
8
|
+
import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
|
|
7
9
|
export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
8
10
|
const [isPending, startTransition] = useTransition();
|
|
9
11
|
const [error, setError] = useState(null);
|
|
@@ -85,7 +87,7 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
|
85
87
|
: []),
|
|
86
88
|
];
|
|
87
89
|
const slotsWithIdx = slots.map((s, i) => ({ ...s, _idx: i }));
|
|
88
|
-
return (_jsx(Container, { children: _jsx(Section, { children: _jsx(Form, { onSubmit: (e) => e.preventDefault(), className: "space-y-6", children: ({ clearErrors }) => (_jsxs(Stack, { gap: "xl", children: [_jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: eventId ? "Edit Lottery" : "Create Lottery" }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Basic Info" }), _jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: (v) => { setTitle(v); clearErrors(); } }), _jsx(FieldInput, { name: "description", label: "Description", value: description, onChange: (v) => { setDescription(v); clearErrors(); } })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Configuration" }), _jsx(FieldSelect, { name: "pricingMode", label: "Pricing Mode", value: pricingMode, options: [
|
|
90
|
+
return (_jsx(Container, { children: _jsx(Section, { children: _jsx(Form, { schema: lotteryConfigFormSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-6", children: ({ clearErrors }) => (_jsxs(Stack, { gap: "xl", children: [_jsx(FormErrorSummary, {}), _jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: eventId ? "Edit Lottery" : "Create Lottery" }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Basic Info" }), _jsx(FieldInput, { name: "title", label: "Title", required: true, value: title, onChange: (v) => { setTitle(v); clearErrors(); } }), _jsx(FieldInput, { name: "description", label: "Description", value: description, onChange: (v) => { setDescription(v); clearErrors(); } })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: "Configuration" }), _jsx(FieldSelect, { name: "pricingMode", label: "Pricing Mode", value: pricingMode, options: [
|
|
89
91
|
{ label: "Uniform (all slots same price)", value: "uniform" },
|
|
90
92
|
{ label: "Variable (per-slot pricing)", value: "variable" },
|
|
91
93
|
], onChange: (v) => setPricingMode(v) }), pricingMode === "uniform" && (_jsx(FieldInput, { name: "uniformPrice", label: "Price per Slot (\u20B9)", type: "number", value: uniformPrice, onChange: (v) => setUniformPrice(v) })), _jsx(FieldInput, { name: "drawWindowMinutes", label: "Draw Window (minutes)", type: "number", value: drawWindowMinutes, onChange: (v) => setDrawWindowMinutes(v) }), _jsx(FieldInput, { name: "maxPullsPerTx", label: "Max Pulls per Transaction", type: "number", value: maxPullsPerTx, onChange: (v) => setMaxPullsPerTx(v) }), _jsx(FieldInput, { name: "maxPullsPerUser", label: "Max Pulls per User", type: "number", value: maxPullsPerUser, onChange: (v) => setMaxPullsPerUser(v) })] }), _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Heading, { level: 2, size: "lg", weight: "semibold", children: ["Slots (", slots.length, ")"] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: addSlot, disabled: slots.length >= 200, children: "+ Add Slot" })] }), _jsx(DataTable, { data: slotsWithIdx, columns: slotColumns, keyExtractor: (s) => String(s.slotNumber), emptyMessage: "No slots yet.", actions: (s) => slots.length > 1 ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeSlot(s._idx), "aria-label": `Remove slot ${s.slotNumber}`, children: "\u00D7" })) : null })] }), error && _jsx(Text, { className: "text-error", size: "sm", role: "alert", children: error }), _jsx(Button, { type: "submit", variant: "primary", isLoading: isPending, onClick: handleSubmit, className: "w-full sm:w-auto", children: isPending ? "Saving…" : eventId ? "Update Lottery" : "Create Lottery" })] })) }) }) }));
|
|
@@ -5,6 +5,8 @@ import { Stack, Row, Text, Heading, Span, } from "../../../../ui";
|
|
|
5
5
|
import { Form, FieldInput, Button } from "../../../../ui";
|
|
6
6
|
import { useApiMutation } from "../../../../client/api/useApiMutation";
|
|
7
7
|
import { apiClient } from "../../../../http";
|
|
8
|
+
import { lotteryPullSchema } from "../../../../features/admin/schemas/admin-user-form";
|
|
9
|
+
import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
|
|
8
10
|
/**
|
|
9
11
|
* User-facing lottery pull form.
|
|
10
12
|
* Submits TX ID + phone + payment time + item number.
|
|
@@ -39,7 +41,7 @@ export function LotteryPullForm({ sourceType, eventId, productId, totalSlots, ma
|
|
|
39
41
|
if (result) {
|
|
40
42
|
return (_jsxs(Stack, { gap: "sm", padding: "md", rounded: "2xl", surface: "success-surface", className: "border border-success/20", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { layout: "inline-flex", color: "inverse", weight: "semibold", className: "bg-success text-[11px] tracking-wide", padding: "pill-sm", rounded: "full", transform: "uppercase", children: "Entry Confirmed" }), _jsxs(Heading, { level: 2, className: "text-success", size: "lg", weight: "bold", children: ["You're #", result.userLotteryNumber, "!"] })] }), _jsxs(Text, { className: "text-success", size: "sm", children: ["You've been assigned", " ", _jsxs(Span, { weight: "bold", children: ["Slot #", result.assignedPrizeSlotNumber] }), result.slotName ? ` — ${result.slotName}` : "", ". Good luck!"] })] }));
|
|
41
43
|
}
|
|
42
|
-
return (_jsx(Form, { onSubmit: (e) => e.preventDefault(), className: "space-y-4", children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, size: "lg", weight: "semibold", children: "Submit Your Entry" }), _jsxs(Text, { size: "sm", color: "muted", children: ["Enter your UPI Transaction ID and payment details to claim your slot.", " ", "You may enter up to ", maxPullsPerUser, " time", maxPullsPerUser === 1 ? "" : "s", "."] }), _jsx(FieldInput, { name: "transactionId", label: "Transaction ID (UPI Ref / Payment Ref)", placeholder: "e.g. 123456789012", required: true, value: transactionId, onChange: (v) => { setTransactionId(v); clearErrors(); } }), _jsx(FieldInput, { name: "paymentTime", label: "Payment Date & Time", type: "datetime-local", required: true, value: paymentTime, onChange: (v) => { setPaymentTime(v); clearErrors(); } }), _jsx(FieldInput, { name: "itemNumber", label: `Slot Number (1 – ${totalSlots})`, type: "number", placeholder: `Enter a number between 1 and ${totalSlots}`, required: true, value: itemNumber, onChange: (v) => { setItemNumber(v); clearErrors(); } }), _jsx(FieldInput, { name: "userPhone", label: "Your Phone Number", type: "tel", placeholder: "e.g. 9876543210", required: true, value: userPhone, onChange: (v) => { setUserPhone(v); clearErrors(); } }), _jsx(Button, { type: "submit", variant: "primary", isLoading: mutation.isPending, onClick: () => {
|
|
44
|
+
return (_jsx(Form, { schema: lotteryPullSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-4", children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FormErrorSummary, {}), _jsx(Heading, { level: 3, size: "lg", weight: "semibold", children: "Submit Your Entry" }), _jsxs(Text, { size: "sm", color: "muted", children: ["Enter your UPI Transaction ID and payment details to claim your slot.", " ", "You may enter up to ", maxPullsPerUser, " time", maxPullsPerUser === 1 ? "" : "s", "."] }), _jsx(FieldInput, { name: "transactionId", label: "Transaction ID (UPI Ref / Payment Ref)", placeholder: "e.g. 123456789012", required: true, value: transactionId, onChange: (v) => { setTransactionId(v); clearErrors(); } }), _jsx(FieldInput, { name: "paymentTime", label: "Payment Date & Time", type: "datetime-local", required: true, value: paymentTime, onChange: (v) => { setPaymentTime(v); clearErrors(); } }), _jsx(FieldInput, { name: "itemNumber", label: `Slot Number (1 – ${totalSlots})`, type: "number", placeholder: `Enter a number between 1 and ${totalSlots}`, required: true, value: itemNumber, onChange: (v) => { setItemNumber(v); clearErrors(); } }), _jsx(FieldInput, { name: "userPhone", label: "Your Phone Number", type: "tel", placeholder: "e.g. 9876543210", required: true, value: userPhone, onChange: (v) => { setUserPhone(v); clearErrors(); } }), _jsx(Button, { type: "submit", variant: "primary", isLoading: mutation.isPending, onClick: () => {
|
|
43
45
|
clearErrors();
|
|
44
46
|
const num = parseInt(itemNumber, 10);
|
|
45
47
|
let valid = true;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const lotteryConfigFormSchema: z.ZodEffects<z.ZodObject<{
|
|
3
|
+
pricingMode: z.ZodEnum<["uniform", "variable"]>;
|
|
4
|
+
uniformPrice: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
slots: z.ZodArray<z.ZodObject<{
|
|
6
|
+
slotNumber: z.ZodNumber;
|
|
7
|
+
name: z.ZodOptional<z.ZodString>;
|
|
8
|
+
price: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
image: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
slotNumber: number;
|
|
12
|
+
image?: string | undefined;
|
|
13
|
+
name?: string | undefined;
|
|
14
|
+
price?: number | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
slotNumber: number;
|
|
17
|
+
image?: string | undefined;
|
|
18
|
+
name?: string | undefined;
|
|
19
|
+
price?: number | undefined;
|
|
20
|
+
}>, "many">;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
slots: {
|
|
23
|
+
slotNumber: number;
|
|
24
|
+
image?: string | undefined;
|
|
25
|
+
name?: string | undefined;
|
|
26
|
+
price?: number | undefined;
|
|
27
|
+
}[];
|
|
28
|
+
pricingMode: "uniform" | "variable";
|
|
29
|
+
uniformPrice?: number | undefined;
|
|
30
|
+
}, {
|
|
31
|
+
slots: {
|
|
32
|
+
slotNumber: number;
|
|
33
|
+
image?: string | undefined;
|
|
34
|
+
name?: string | undefined;
|
|
35
|
+
price?: number | undefined;
|
|
36
|
+
}[];
|
|
37
|
+
pricingMode: "uniform" | "variable";
|
|
38
|
+
uniformPrice?: number | undefined;
|
|
39
|
+
}>, {
|
|
40
|
+
slots: {
|
|
41
|
+
slotNumber: number;
|
|
42
|
+
image?: string | undefined;
|
|
43
|
+
name?: string | undefined;
|
|
44
|
+
price?: number | undefined;
|
|
45
|
+
}[];
|
|
46
|
+
pricingMode: "uniform" | "variable";
|
|
47
|
+
uniformPrice?: number | undefined;
|
|
48
|
+
}, {
|
|
49
|
+
slots: {
|
|
50
|
+
slotNumber: number;
|
|
51
|
+
image?: string | undefined;
|
|
52
|
+
name?: string | undefined;
|
|
53
|
+
price?: number | undefined;
|
|
54
|
+
}[];
|
|
55
|
+
pricingMode: "uniform" | "variable";
|
|
56
|
+
uniformPrice?: number | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
export type LotteryConfigFormValues = z.infer<typeof lotteryConfigFormSchema>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `LotteryAdminEditView` rendered `<Form>` with no schema. A lottery
|
|
3
|
+
* config is priced money: `uniformPrice` was
|
|
4
|
+
* `Math.round(parseFloat(x) * 100) / 100 || 0`, which turns any
|
|
5
|
+
* unparseable input into **zero** rather than an error — so a typo in the
|
|
6
|
+
* price box published a free lottery.
|
|
7
|
+
*
|
|
8
|
+
* `slots` is the other half: each row carries a `slotNumber`, a name and
|
|
9
|
+
* a price, and a duplicate slot number makes two buyers think they own
|
|
10
|
+
* the same slot.
|
|
11
|
+
*
|
|
12
|
+
* WHAT: The config contract.
|
|
13
|
+
*
|
|
14
|
+
* @tag domain:lottery
|
|
15
|
+
* @tag layer:schema
|
|
16
|
+
* @tag pattern:none
|
|
17
|
+
* @tag access:isomorphic
|
|
18
|
+
* @tag consumers:LotteryAdminEditView
|
|
19
|
+
* @tag sideEffects:none
|
|
20
|
+
*/
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
import { annotate } from "../../../../features/shell/field-ui-meta";
|
|
23
|
+
const slotSchema = z.object({
|
|
24
|
+
slotNumber: z.coerce.number().int().min(1, "Slot numbers start at 1."),
|
|
25
|
+
name: z.string().trim().max(120).optional(),
|
|
26
|
+
price: z.coerce.number().min(0, "A slot price cannot be negative.").optional(),
|
|
27
|
+
image: z.string().trim().optional(),
|
|
28
|
+
});
|
|
29
|
+
export const lotteryConfigFormSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
pricingMode: annotate(z.enum(["uniform", "variable"]), {
|
|
32
|
+
section: "pricing", sectionLabel: "Pricing", sectionRequired: true, quick: true, order: 1, row: "pair",
|
|
33
|
+
}),
|
|
34
|
+
/*
|
|
35
|
+
* 🛑 Coerced with a real error, NOT `|| 0`. The previous
|
|
36
|
+
* `parseFloat(x) || 0` silently turned "12,00" or "twelve" into a free
|
|
37
|
+
* lottery — the failure was indistinguishable from deliberately pricing
|
|
38
|
+
* it at zero.
|
|
39
|
+
*/
|
|
40
|
+
uniformPrice: annotate(z.coerce
|
|
41
|
+
.number({ invalid_type_error: "Enter the slot price as a number." })
|
|
42
|
+
.min(0, "A price cannot be negative.")
|
|
43
|
+
.max(10000000, "That looks like a typo.")
|
|
44
|
+
.optional(), { section: "pricing", order: 2, row: "pair", kind: "number" }),
|
|
45
|
+
slots: annotate(z.array(slotSchema).min(1, "A lottery needs at least one slot.").max(1000), {
|
|
46
|
+
section: "slots", sectionLabel: "Slots", sectionRequired: true, order: 1, row: "full", kind: "list",
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
.superRefine((v, ctx) => {
|
|
50
|
+
// Uniform pricing with no price is the free-lottery case the `|| 0` used
|
|
51
|
+
// to create silently.
|
|
52
|
+
if (v.pricingMode === "uniform" && (v.uniformPrice === undefined || v.uniformPrice <= 0)) {
|
|
53
|
+
ctx.addIssue({
|
|
54
|
+
code: z.ZodIssueCode.custom,
|
|
55
|
+
path: ["uniformPrice"],
|
|
56
|
+
message: "Set a slot price above zero, or switch to variable pricing.",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Two buyers cannot own the same slot number.
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const [i, s] of v.slots.entries()) {
|
|
62
|
+
if (seen.has(s.slotNumber)) {
|
|
63
|
+
ctx.addIssue({
|
|
64
|
+
code: z.ZodIssueCode.custom,
|
|
65
|
+
path: ["slots", i, "slotNumber"],
|
|
66
|
+
message: `Slot ${s.slotNumber} appears more than once.`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
seen.add(s.slotNumber);
|
|
70
|
+
}
|
|
71
|
+
});
|
package/dist/client.d.ts
CHANGED
|
@@ -436,3 +436,11 @@ export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS
|
|
|
436
436
|
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";
|
|
437
437
|
export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
|
|
438
438
|
export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
|
|
439
|
+
export { orderCancelSchema, paymentProofSchema, } from "./features/orders/schemas/buyer-forms";
|
|
440
|
+
export { scamReportFormSchema } from "./features/scams/schemas/report-form";
|
|
441
|
+
export { sublistingCategoryFormSchema } from "./features/store-extensions/schemas/sublisting-category-form";
|
|
442
|
+
export { adminAddressFormSchema, adminStoreUpdateSchema, announcementBarSchema, } from "./features/admin/schemas/admin-editor-forms";
|
|
443
|
+
export { adminOrderUpdateSchema, employeeInviteSchema, hardBanReasonSchema, featureFlagsUpdateSchema, adminAdFormSchema, } from "./features/admin/schemas/admin-ops-forms";
|
|
444
|
+
export { changeEmailSchema } from "./features/account/schemas/index";
|
|
445
|
+
export { adminUserUpdateSchema, whatsappSettingsSchema, lotteryPullSchema, } from "./features/admin/schemas/admin-user-form";
|
|
446
|
+
export { siteSettingsFormSchema } from "./features/admin/schemas/site-settings-form";
|
package/dist/client.js
CHANGED
|
@@ -422,3 +422,11 @@ export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS
|
|
|
422
422
|
export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, } from "./_internal/shared/checkout/lanes";
|
|
423
423
|
export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
|
|
424
424
|
export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
|
|
425
|
+
export { orderCancelSchema, paymentProofSchema, } from "./features/orders/schemas/buyer-forms";
|
|
426
|
+
export { scamReportFormSchema } from "./features/scams/schemas/report-form";
|
|
427
|
+
export { sublistingCategoryFormSchema } from "./features/store-extensions/schemas/sublisting-category-form";
|
|
428
|
+
export { adminAddressFormSchema, adminStoreUpdateSchema, announcementBarSchema, } from "./features/admin/schemas/admin-editor-forms";
|
|
429
|
+
export { adminOrderUpdateSchema, employeeInviteSchema, hardBanReasonSchema, featureFlagsUpdateSchema, adminAdFormSchema, } from "./features/admin/schemas/admin-ops-forms";
|
|
430
|
+
export { changeEmailSchema } from "./features/account/schemas/index";
|
|
431
|
+
export { adminUserUpdateSchema, whatsappSettingsSchema, lotteryPullSchema, } from "./features/admin/schemas/admin-user-form";
|
|
432
|
+
export { siteSettingsFormSchema } from "./features/admin/schemas/site-settings-form";
|
|
@@ -205,3 +205,26 @@ export declare const updateProfileSchema: z.ZodObject<{
|
|
|
205
205
|
phoneNumber?: string | undefined;
|
|
206
206
|
profileIsPublic?: boolean | undefined;
|
|
207
207
|
}>;
|
|
208
|
+
/**
|
|
209
|
+
* Changing the account email.
|
|
210
|
+
*
|
|
211
|
+
* Both fields were bare `<Input required>` on `/user/settings` — an HTML
|
|
212
|
+
* attribute any programmatic submit bypasses — on a form that changes the
|
|
213
|
+
* address every future sign-in and password reset goes to.
|
|
214
|
+
*
|
|
215
|
+
* The current password is required because re-authentication is what stops a
|
|
216
|
+
* stolen session cookie from redirecting an account to an attacker's inbox;
|
|
217
|
+
* the length floor is deliberately low, since it is checked against a stored
|
|
218
|
+
* credential, not chosen here.
|
|
219
|
+
*/
|
|
220
|
+
export declare const changeEmailSchema: z.ZodObject<{
|
|
221
|
+
newEmail: z.ZodString;
|
|
222
|
+
emailPassword: z.ZodString;
|
|
223
|
+
}, "strip", z.ZodTypeAny, {
|
|
224
|
+
newEmail: string;
|
|
225
|
+
emailPassword: string;
|
|
226
|
+
}, {
|
|
227
|
+
newEmail: string;
|
|
228
|
+
emailPassword: string;
|
|
229
|
+
}>;
|
|
230
|
+
export type ChangeEmailValues = z.infer<typeof changeEmailSchema>;
|
|
@@ -56,3 +56,24 @@ export const updateProfileSchema = z.object({
|
|
|
56
56
|
bio: z.string().max(500, "Bio must be 500 characters or fewer").optional(),
|
|
57
57
|
profileIsPublic: z.boolean().optional(),
|
|
58
58
|
});
|
|
59
|
+
/**
|
|
60
|
+
* Changing the account email.
|
|
61
|
+
*
|
|
62
|
+
* Both fields were bare `<Input required>` on `/user/settings` — an HTML
|
|
63
|
+
* attribute any programmatic submit bypasses — on a form that changes the
|
|
64
|
+
* address every future sign-in and password reset goes to.
|
|
65
|
+
*
|
|
66
|
+
* The current password is required because re-authentication is what stops a
|
|
67
|
+
* stolen session cookie from redirecting an account to an attacker's inbox;
|
|
68
|
+
* the length floor is deliberately low, since it is checked against a stored
|
|
69
|
+
* credential, not chosen here.
|
|
70
|
+
*/
|
|
71
|
+
export const changeEmailSchema = z.object({
|
|
72
|
+
newEmail: z
|
|
73
|
+
.string()
|
|
74
|
+
.trim()
|
|
75
|
+
.toLowerCase()
|
|
76
|
+
.min(1, "Enter the new email address.")
|
|
77
|
+
.email("That doesn't look like an email address."),
|
|
78
|
+
emailPassword: z.string().min(1, "Enter your current password to confirm."),
|
|
79
|
+
});
|
|
@@ -7,6 +7,8 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
7
7
|
import { Alert, Button, Checkbox, Div, Form, FormActions, Input, Select, Span, Stack, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
|
|
8
8
|
import { apiClient } from "../../../http";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { adminAdFormSchema } from "../schemas/admin-ops-forms";
|
|
11
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
10
12
|
const __P = {
|
|
11
13
|
p3: "p-[var(--appkit-space-3)]",
|
|
12
14
|
};
|
|
@@ -223,14 +225,14 @@ export function AdminAdEditorView({ adId, endpointBase = ADMIN_ENDPOINTS.ADS, la
|
|
|
223
225
|
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: labels.title ?? (adId ? "Edit Ad" : "Create Ad"), sections: [
|
|
224
226
|
metaQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ad metadata", children: metaQuery.error instanceof Error ? metaQuery.error.message : "Unknown error" })) : null,
|
|
225
227
|
adQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ad", children: adQuery.error instanceof Error ? adQuery.error.message : "Unknown error" })) : null,
|
|
226
|
-
_jsxs(Form, { onSubmit: (event) => {
|
|
228
|
+
_jsxs(Form, { schema: adminAdFormSchema, onSubmit: (event) => {
|
|
227
229
|
event.preventDefault();
|
|
228
230
|
if (blockedByPublishValidation) {
|
|
229
231
|
showToast("Cannot save in publish-ready status until validation issues are fixed.", "warning");
|
|
230
232
|
return;
|
|
231
233
|
}
|
|
232
234
|
saveMutation.mutate();
|
|
233
|
-
}, spacing: "md", children: [_jsx(Input, { label: "Ad name", value: name, onChange: (event) => setName(event.target.value), placeholder: "Summer Campaign" }), _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-3", children: [_jsx(Select, { label: "Provider", value: provider, options: [
|
|
235
|
+
}, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Ad name", value: name, onChange: (event) => setName(event.target.value), placeholder: "Summer Campaign" }), _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-3", children: [_jsx(Select, { label: "Provider", value: provider, options: [
|
|
234
236
|
{ label: "Manual", value: "manual" },
|
|
235
237
|
{ label: "AdSense", value: "adsense" },
|
|
236
238
|
{ label: "Third Party", value: "thirdParty" },
|
|
@@ -9,6 +9,8 @@ import { apiClient } from "../../../http";
|
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
11
|
import { INDIAN_STATES } from "../../account/constants/addresses";
|
|
12
|
+
import { adminAddressFormSchema } from "../schemas/admin-editor-forms";
|
|
13
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
12
14
|
const STATE_OPTIONS = INDIAN_STATES.map((s) => ({ value: s, label: s }));
|
|
13
15
|
// ── Section heading helper ─────────────────────────────────────────────────────
|
|
14
16
|
function SectionHeading({ children }) {
|
|
@@ -105,10 +107,10 @@ export function AdminAddressEditorView({ addressId, onSaved, onDeleted, ...rest
|
|
|
105
107
|
// ── Action sidebar (desktop) ───────────────────────────────────────────────
|
|
106
108
|
const actionSidebar = (_jsxs(Card, { variant: "outlined", padding: "md", spacing: "sm", children: [_jsx(Button, { type: "submit", form: "address-editor-form", className: "w-full", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? ACTIONS.ADMIN["save-changes"].label : "Create address" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", className: "w-full", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete address" }))] }));
|
|
107
109
|
// ── Form sections ──────────────────────────────────────────────────────────
|
|
108
|
-
const formContent = (_jsxs(Form, { id: "address-editor-form", onSubmit: (e) => {
|
|
110
|
+
const formContent = (_jsxs(Form, { schema: adminAddressFormSchema, id: "address-editor-form", onSubmit: (e) => {
|
|
109
111
|
e.preventDefault();
|
|
110
112
|
saveMutation.mutate();
|
|
111
|
-
}, spacing: "lg", children: [_jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(SectionHeading, { children: "Ownership" }), _jsxs(Stack, { gap: "md", children: [_jsx(Row, { gap: "lg", children: ["user", "store"].map((type) => (_jsxs("label", { className: "flex cursor-pointer items-center gap-[var(--appkit-space-2)]", children: [_jsx(Checkbox, { bare: true, type: "radio", name: "ownerType", value: type, checked: form.ownerType === type, onChange: () => {
|
|
113
|
+
}, spacing: "lg", children: [_jsx(FormErrorSummary, {}), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(SectionHeading, { children: "Ownership" }), _jsxs(Stack, { gap: "md", children: [_jsx(Row, { gap: "lg", children: ["user", "store"].map((type) => (_jsxs("label", { className: "flex cursor-pointer items-center gap-[var(--appkit-space-2)]", children: [_jsx(Checkbox, { bare: true, type: "radio", name: "ownerType", value: type, checked: form.ownerType === type, onChange: () => {
|
|
112
114
|
set("ownerType", type);
|
|
113
115
|
set("ownerId", "");
|
|
114
116
|
}, className: "accent-[var(--appkit-color-primary)]" }), _jsx(Span, { size: "sm", className: "text-[var(--appkit-color-text)]", transform: "capitalize", children: type })] }, type))) }), _jsx(Input, { label: form.ownerType === "user" ? "User ID" : "Store ID (slug)", value: form.ownerId, onChange: (e) => set("ownerId", e.target.value), required: true, placeholder: form.ownerType === "user" ? "user-mohsin-c" : "store-pokemon-palace", helperText: form.ownerType === "user"
|
|
@@ -7,6 +7,8 @@ import { Button, Checkbox, ConfirmDeleteModal, Details, Div, Form, FormActions,
|
|
|
7
7
|
import { apiClient } from "../../../http";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { PERMISSION_GROUPS, PERMISSION_DOMAINS, getPermissionsForDomain, formatPermLabel, } from "../../auth/permissions/constants";
|
|
10
|
+
import { employeeInviteSchema } from "../schemas/admin-ops-forms";
|
|
11
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
10
12
|
const __O = {
|
|
11
13
|
yAuto: "overflow-y-auto",
|
|
12
14
|
};
|
|
@@ -130,5 +132,5 @@ export function AdminEmployeeEditorView({ open, onClose, mode, userId, displayNa
|
|
|
130
132
|
else
|
|
131
133
|
updateMutation.mutate();
|
|
132
134
|
};
|
|
133
|
-
return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: mode === "invite" ? "Invite Employee" : `Edit — ${displayName ?? "Employee"}`, children: _jsxs(Form, { onSubmit: handleSubmit, spacing: "lg", padding: "md", children: [mode === "invite" && (_jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Email address" }), _jsx(Input, { type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value), placeholder: "employee@example.com" })] })), _jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Permission group" }), _jsx(Select, { value: group, onChange: (e) => applyGroupPreset(e.target.value), options: GROUP_OPTIONS }), _jsx(Text, { size: "xs", color: "muted", children: "Selecting a group auto-fills the permissions below. You can still customise individual permissions." })] }), _jsx(PermissionDomainsPanel, { permissions: permissions, togglePerm: togglePerm }), _jsxs(FormActions, { children: [_jsx(Button, { type: "submit", variant: "primary", disabled: isBusy, isLoading: isBusy, children: mode === "invite" ? "Send Invite" : "Save" }), mode === "edit" && userId && (_jsx(Button, { type: "button", variant: "danger", disabled: isBusy, onClick: () => setRevokeOpen(true), children: "Revoke Access" }))] })] }) }), mode === "edit" && (_jsx(ConfirmDeleteModal, { isOpen: revokeOpen, onClose: () => setRevokeOpen(false), onConfirm: () => revokeMutation.mutate(), isDeleting: revokeMutation.isPending, title: "Revoke employee access?", message: `${displayName ?? "This employee"} will lose admin panel access immediately. Their user account remains active — only their role is reset to user.`, confirmText: "Revoke Access", variant: "danger" }))] }));
|
|
135
|
+
return (_jsxs(_Fragment, { children: [_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: mode === "invite" ? "Invite Employee" : `Edit — ${displayName ?? "Employee"}`, children: _jsxs(Form, { schema: employeeInviteSchema, onSubmit: handleSubmit, spacing: "lg", padding: "md", children: [_jsx(FormErrorSummary, {}), mode === "invite" && (_jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Email address" }), _jsx(Input, { type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value), placeholder: "employee@example.com" })] })), _jsxs(Stack, { gap: "xs", className: "", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Permission group" }), _jsx(Select, { value: group, onChange: (e) => applyGroupPreset(e.target.value), options: GROUP_OPTIONS }), _jsx(Text, { size: "xs", color: "muted", children: "Selecting a group auto-fills the permissions below. You can still customise individual permissions." })] }), _jsx(PermissionDomainsPanel, { permissions: permissions, togglePerm: togglePerm }), _jsxs(FormActions, { children: [_jsx(Button, { type: "submit", variant: "primary", disabled: isBusy, isLoading: isBusy, children: mode === "invite" ? "Send Invite" : "Save" }), mode === "edit" && userId && (_jsx(Button, { type: "button", variant: "danger", disabled: isBusy, onClick: () => setRevokeOpen(true), children: "Revoke Access" }))] })] }) }), mode === "edit" && (_jsx(ConfirmDeleteModal, { isOpen: revokeOpen, onClose: () => setRevokeOpen(false), onConfirm: () => revokeMutation.mutate(), isDeleting: revokeMutation.isPending, title: "Revoke employee access?", message: `${displayName ?? "This employee"} will lose admin panel access immediately. Their user account remains active — only their role is reset to user.`, confirmText: "Revoke Access", variant: "danger" }))] }));
|
|
134
136
|
}
|
|
@@ -6,6 +6,8 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
6
6
|
import { Alert, Button, Div, Form, FormActions, Input, Row, Stack, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
import { apiClient } from "../../../http";
|
|
9
|
+
import { featureFlagsUpdateSchema } from "../schemas/admin-ops-forms";
|
|
10
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
9
11
|
const __O = {
|
|
10
12
|
hidden: "overflow-hidden",
|
|
11
13
|
};
|
|
@@ -96,10 +98,10 @@ export function AdminFeatureFlagsView({ labels = {}, renderFlags, ...rest }) {
|
|
|
96
98
|
showToast("Failed to save feature flags.", "error");
|
|
97
99
|
},
|
|
98
100
|
});
|
|
99
|
-
const defaultFlags = () => (_jsxs(Form, { onSubmit: (event) => {
|
|
101
|
+
const defaultFlags = () => (_jsxs(Form, { schema: featureFlagsUpdateSchema, onSubmit: (event) => {
|
|
100
102
|
event.preventDefault();
|
|
101
103
|
saveFlags.mutate();
|
|
102
|
-
}, spacing: "md", children: [_jsx(AccordionSection, { title: "Platform Features", children: PLATFORM_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getPlatformFlag(key), onChange: (val) => setPlatformFlag(key, val), rollout: rollouts[key], onRolloutChange: (val) => setRollouts((prev) => ({ ...prev, [key]: val })), showRollout: true }, key))) }), _jsx(AccordionSection, { title: "Listing Types", children: LISTING_TYPE_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getListingTypeFlag(key), onChange: (val) => setListingTypeFlag(key, val), showRollout: false }, key))) }), _jsx(AccordionSection, { title: "Category Types", children: CATEGORY_TYPE_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getCategoryTypeFlag(key), onChange: (val) => setCategoryTypeFlag(key, val), showRollout: false }, key))) }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => {
|
|
104
|
+
}, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsx(AccordionSection, { title: "Platform Features", children: PLATFORM_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getPlatformFlag(key), onChange: (val) => setPlatformFlag(key, val), rollout: rollouts[key], onRolloutChange: (val) => setRollouts((prev) => ({ ...prev, [key]: val })), showRollout: true }, key))) }), _jsx(AccordionSection, { title: "Listing Types", children: LISTING_TYPE_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getListingTypeFlag(key), onChange: (val) => setListingTypeFlag(key, val), showRollout: false }, key))) }), _jsx(AccordionSection, { title: "Category Types", children: CATEGORY_TYPE_FLAGS.map(({ key, label, desc }) => (_jsx(FlagRow, { label: label, desc: desc, checked: getCategoryTypeFlag(key), onChange: (val) => setCategoryTypeFlag(key, val), showRollout: false }, key))) }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => {
|
|
103
105
|
setFlags(data?.flags ?? {});
|
|
104
106
|
setRollouts(data?.rollouts ?? {});
|
|
105
107
|
}, disabled: saveFlags.isPending, children: "Reset" }), _jsx(Button, { type: "submit", isLoading: saveFlags.isPending, disabled: saveFlags.isPending, children: "Save All Flags" })] })] }));
|
|
@@ -12,6 +12,8 @@ import { apiClient } from "../../../http";
|
|
|
12
12
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
13
13
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
14
14
|
import { toCurrency } from "../hooks/useAdminListingData";
|
|
15
|
+
import { adminOrderUpdateSchema } from "../schemas/admin-ops-forms";
|
|
16
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
|
|
15
17
|
const STATUS_OPTIONS = [
|
|
16
18
|
{ label: "Pending", value: "pending" },
|
|
17
19
|
{ label: "Processing", value: "processing" },
|
|
@@ -146,8 +148,8 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
146
148
|
setIsRejectingFraud(false);
|
|
147
149
|
}
|
|
148
150
|
};
|
|
149
|
-
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsxs(Form, { onSubmit: (e) => {
|
|
151
|
+
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsxs(Form, { schema: adminOrderUpdateSchema, onSubmit: (e) => {
|
|
150
152
|
e.preventDefault();
|
|
151
153
|
saveMutation.mutate();
|
|
152
|
-
}, spacing: "md", padding: "md", children: [addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(Select, { label: "Order status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Input, { label: "Tracking number (optional)", value: trackingNumber, onChange: (e) => setTrackingNumber(e.target.value), placeholder: "e.g. DEL1234567890IN" }), _jsx(Select, { label: "Carrier (optional)", options: CARRIER_OPTIONS, value: carrier, onValueChange: setCarrier }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Internal note (optional)" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Reason for status change, escalation notes\u2026" })] }), (status === "refunded" || status === "return_requested") && (_jsx(Input, { label: "Refund amount \u20B9 (optional)", type: "number", min: "0", step: "0.01", value: refundAmount, onChange: (e) => setRefundAmount(e.target.value), placeholder: "e.g. 499.00" })), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note (required for re-upload / reject)" }), _jsx(Textarea, { value: reviewNote, onChange: (e) => setReviewNote(e.target.value), rows: 2, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" })] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload || !reviewNote.trim(), variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud || !reviewNote.trim(), variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !orderId || saveMutation.isPending, children: "Save changes" })] })] }) }));
|
|
154
|
+
}, spacing: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(Select, { label: "Order status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Input, { label: "Tracking number (optional)", value: trackingNumber, onChange: (e) => setTrackingNumber(e.target.value), placeholder: "e.g. DEL1234567890IN" }), _jsx(Select, { label: "Carrier (optional)", options: CARRIER_OPTIONS, value: carrier, onValueChange: setCarrier }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Internal note (optional)" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Reason for status change, escalation notes\u2026" })] }), (status === "refunded" || status === "return_requested") && (_jsx(Input, { label: "Refund amount \u20B9 (optional)", type: "number", min: "0", step: "0.01", value: refundAmount, onChange: (e) => setRefundAmount(e.target.value), placeholder: "e.g. 499.00" })), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note (required for re-upload / reject)" }), _jsx(Textarea, { value: reviewNote, onChange: (e) => setReviewNote(e.target.value), rows: 2, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" })] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload || !reviewNote.trim(), variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud || !reviewNote.trim(), variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !orderId || saveMutation.isPending, children: "Save changes" })] })] }) }));
|
|
153
155
|
}
|