@mohasinac/appkit 4.16.1 → 4.18.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.
Files changed (43) hide show
  1. package/dist/client.d.ts +7 -0
  2. package/dist/client.js +7 -0
  3. package/dist/features/admin/components/AdminAdEditorView.js +4 -2
  4. package/dist/features/admin/components/AdminAddressEditorView.js +4 -2
  5. package/dist/features/admin/components/AdminEmployeeEditorView.js +3 -1
  6. package/dist/features/admin/components/AdminFeatureFlagsView.js +4 -2
  7. package/dist/features/admin/components/AdminOrderEditorView.js +4 -2
  8. package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +18 -6
  9. package/dist/features/admin/components/AdminSiteView.js +4 -2
  10. package/dist/features/admin/components/AdminStoreEditorView.js +11 -9
  11. package/dist/features/admin/components/AdminUsersView.js +4 -2
  12. package/dist/features/admin/components/BrandQuickCreateForm.js +13 -5
  13. package/dist/features/admin/components/CategoryQuickCreateForm.js +13 -5
  14. package/dist/features/admin/schemas/admin-editor-forms.d.ts +119 -0
  15. package/dist/features/admin/schemas/admin-editor-forms.js +141 -0
  16. package/dist/features/admin/schemas/admin-ops-forms.d.ts +123 -0
  17. package/dist/features/admin/schemas/admin-ops-forms.js +147 -0
  18. package/dist/features/admin/schemas/small-forms.d.ts +54 -0
  19. package/dist/features/admin/schemas/small-forms.js +85 -0
  20. package/dist/features/auth/components/ForgotPasswordView.js +2 -1
  21. package/dist/features/auth/components/LoginForm.js +2 -1
  22. package/dist/features/auth/components/RegisterForm.js +2 -1
  23. package/dist/features/auth/components/ResetPasswordView.js +2 -1
  24. package/dist/features/consultation/components/ConsultationForm.js +3 -1
  25. package/dist/features/consultation/schemas/index.d.ts +4 -4
  26. package/dist/features/consultation/schemas/index.js +22 -5
  27. package/dist/features/contact/schemas/inquiry-forms.d.ts +13 -0
  28. package/dist/features/contact/schemas/inquiry-forms.js +34 -0
  29. package/dist/features/corporate/components/CorporateInquiryForm.js +3 -1
  30. package/dist/features/corporate/schemas/index.d.ts +4 -1
  31. package/dist/features/corporate/schemas/index.js +38 -8
  32. package/dist/features/events/components/EventRaffleEntryForm.js +3 -1
  33. package/dist/features/homepage/components/NewsletterBanner.js +3 -1
  34. package/dist/features/orders/schemas/buyer-forms.d.ts +46 -0
  35. package/dist/features/orders/schemas/buyer-forms.js +81 -0
  36. package/dist/features/products/components/MakeOfferButton.js +1 -1
  37. package/dist/features/scams/schemas/report-form.d.ts +63 -0
  38. package/dist/features/scams/schemas/report-form.js +88 -0
  39. package/dist/features/store-extensions/schemas/sublisting-category-form.d.ts +15 -0
  40. package/dist/features/store-extensions/schemas/sublisting-category-form.js +41 -0
  41. package/dist/index.d.ts +7 -0
  42. package/dist/index.js +7 -0
  43. package/package.json +1 -1
@@ -0,0 +1,141 @@
1
+ /*
2
+ * WHY: A cluster of admin editors rendered `<Form>` with no schema. Two of
3
+ * them write PII, and one changes a store's standing on the platform.
4
+ *
5
+ * `AdminAddressEditorView` is the PII one: `fullName`, `phone` and the
6
+ * address lines are all encrypted at rest, and none was checked — a
7
+ * postal code could be any string, and `ownerId` (which decides WHOSE
8
+ * address this is) was free text.
9
+ *
10
+ * `AdminStoreEditorView` sets `storeStatus`, `isVerified`, `isFeatured`
11
+ * and `capabilities`. Recurrent Root Cause #38 lived here: saving any
12
+ * field re-sent every other one, so an unrelated edit could silently
13
+ * un-verify a store.
14
+ *
15
+ * WHAT: One schema per editor.
16
+ *
17
+ * EXPORTS:
18
+ * adminAddressFormSchema, adminStoreUpdateSchema, announcementBarSchema,
19
+ * and their inferred value types
20
+ *
21
+ * @tag domain:admin
22
+ * @tag layer:schema
23
+ * @tag pattern:none
24
+ * @tag access:isomorphic
25
+ * @tag consumers:AdminAddressEditorView,AdminStoreEditorView,AdminSiteView
26
+ * @tag sideEffects:none
27
+ */
28
+ import { z } from "zod";
29
+ import { annotate } from "../../shell/field-ui-meta";
30
+ const PHONE_RE = /^(\+?91[-\s]?)?[6-9]\d{9}$/;
31
+ /** Indian PIN: six digits, never starting at zero. */
32
+ const PIN_RE = /^[1-9]\d{5}$/;
33
+ /**
34
+ * An address written by an admin on someone's behalf.
35
+ *
36
+ * Shapes match `AddressDocument` (`addressLine1`, not `line1`) — deliberately
37
+ * NOT `userAddressSchema`, which is the account-side view of the same data
38
+ * under different field names. Reusing that one here would have meant
39
+ * translating on every write.
40
+ */
41
+ export const adminAddressFormSchema = z.object({
42
+ ownerType: annotate(z.enum(["user", "store"]), {
43
+ section: "owner", sectionLabel: "Belongs to", sectionRequired: true, quick: true, order: 1, row: "pair",
44
+ }),
45
+ ownerId: annotate(z.string().trim().min(1, "Choose who this address belongs to."), {
46
+ section: "owner", quick: true, order: 2, row: "pair",
47
+ }),
48
+ label: annotate(z.string().trim().max(60, "Keep the label short — e.g. Home, Warehouse.").optional(), {
49
+ section: "address", sectionLabel: "Address", order: 1, row: "pair",
50
+ }),
51
+ fullName: annotate(z.string().trim().min(2, "A recipient name is required.").max(120), {
52
+ section: "address", sectionRequired: true, quick: true, order: 2, row: "pair",
53
+ }),
54
+ phone: annotate(z.string().trim().regex(PHONE_RE, "Enter a 10-digit Indian mobile number."), {
55
+ section: "address", quick: true, order: 3, row: "pair",
56
+ }),
57
+ addressLine1: annotate(z.string().trim().min(3, "Street address is required.").max(200), {
58
+ section: "address", quick: true, order: 4, row: "full",
59
+ }),
60
+ addressLine2: annotate(z.string().trim().max(200).optional(), {
61
+ section: "address", order: 5, row: "full",
62
+ }),
63
+ landmark: annotate(z.string().trim().max(120).optional(), {
64
+ section: "address", order: 6, row: "pair",
65
+ }),
66
+ city: annotate(z.string().trim().min(2, "City is required.").max(80), {
67
+ section: "address", order: 7, row: "pair",
68
+ }),
69
+ state: annotate(z.string().trim().min(2, "State is required.").max(80), {
70
+ section: "address", order: 8, row: "pair",
71
+ }),
72
+ // A wrong PIN is the single most common reason a parcel is undeliverable,
73
+ // and it was previously any string at all.
74
+ postalCode: annotate(z.string().trim().regex(PIN_RE, "Enter a 6-digit PIN code."), {
75
+ section: "address", order: 9, row: "pair",
76
+ }),
77
+ country: annotate(z.string().trim().min(2).max(60), {
78
+ section: "address", order: 10, row: "pair",
79
+ }),
80
+ isDefault: annotate(z.boolean().optional(), {
81
+ section: "address", order: 11, row: "quarter",
82
+ }),
83
+ });
84
+ /**
85
+ * An admin changing a store's standing.
86
+ *
87
+ * `suspensionReason` is required when — and only when — the status is
88
+ * `suspended`: suspending a seller without a recorded reason leaves them, and
89
+ * the next admin, with nothing to appeal or review.
90
+ */
91
+ export const adminStoreUpdateSchema = z
92
+ .object({
93
+ storeStatus: annotate(z.enum(["pending", "active", "suspended", "closed"]), {
94
+ section: "standing", sectionLabel: "Store standing", sectionRequired: true, quick: true, order: 1, row: "pair",
95
+ }),
96
+ isVerified: annotate(z.boolean().optional(), {
97
+ section: "standing", order: 2, row: "quarter",
98
+ }),
99
+ isFeatured: annotate(z.boolean().optional(), {
100
+ section: "standing", order: 3, row: "quarter",
101
+ }),
102
+ suspensionReason: annotate(z.string().trim().max(500).optional(), {
103
+ section: "standing", order: 4, row: "full", kind: "textarea",
104
+ }),
105
+ capabilities: annotate(z.array(z.string().min(1)).max(50).optional(), {
106
+ section: "capabilities", sectionLabel: "Capabilities", order: 1, row: "full", kind: "list",
107
+ }),
108
+ adminNotes: annotate(z.string().trim().max(2000).optional(), {
109
+ section: "capabilities", order: 2, row: "full", kind: "textarea",
110
+ help: "Internal only — never shown to the store owner or the public.",
111
+ }),
112
+ })
113
+ .superRefine((v, ctx) => {
114
+ if (v.storeStatus === "suspended" && !v.suspensionReason?.trim()) {
115
+ ctx.addIssue({
116
+ code: z.ZodIssueCode.custom,
117
+ path: ["suspensionReason"],
118
+ message: "Record why this store is being suspended.",
119
+ });
120
+ }
121
+ });
122
+ /**
123
+ * The homepage announcement bar.
124
+ *
125
+ * 🛑 `message` is the canonical key. The main admin editor saved this copy
126
+ * under `text` until 2026-08-24 — a key no renderer has ever read — so
127
+ * admin-authored announcements saved successfully and never appeared. Naming
128
+ * it here is what stops that recurring.
129
+ */
130
+ export const announcementBarSchema = z.object({
131
+ message: annotate(z.string().trim().max(300, "Keep the announcement under 300 characters.").optional(), {
132
+ section: "announcement", sectionLabel: "Announcement bar", sectionRequired: true,
133
+ quick: true, order: 1, row: "full",
134
+ }),
135
+ enabled: annotate(z.boolean().optional(), {
136
+ section: "announcement", order: 2, row: "quarter",
137
+ }),
138
+ link: annotate(z.string().trim().max(300).optional(), {
139
+ section: "announcement", order: 3, row: "full",
140
+ }),
141
+ });
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ export declare const adminOrderUpdateSchema: z.ZodObject<{
3
+ status: z.ZodEnum<[string, ...string[]]>;
4
+ trackingNumber: z.ZodOptional<z.ZodString>;
5
+ carrier: z.ZodOptional<z.ZodString>;
6
+ refundAmount: z.ZodOptional<z.ZodNumber>;
7
+ notes: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ status: string;
10
+ notes?: string | undefined;
11
+ trackingNumber?: string | undefined;
12
+ refundAmount?: number | undefined;
13
+ carrier?: string | undefined;
14
+ }, {
15
+ status: string;
16
+ notes?: string | undefined;
17
+ trackingNumber?: string | undefined;
18
+ refundAmount?: number | undefined;
19
+ carrier?: string | undefined;
20
+ }>;
21
+ export type AdminOrderUpdateValues = z.infer<typeof adminOrderUpdateSchema>;
22
+ /** Inviting a team member — an email plus the permissions it grants. */
23
+ export declare const employeeInviteSchema: z.ZodObject<{
24
+ email: z.ZodString;
25
+ permissionGroup: z.ZodString;
26
+ permissions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
27
+ }, "strip", z.ZodTypeAny, {
28
+ email: string;
29
+ permissionGroup: string;
30
+ permissions?: string[] | undefined;
31
+ }, {
32
+ email: string;
33
+ permissionGroup: string;
34
+ permissions?: string[] | undefined;
35
+ }>;
36
+ export type EmployeeInviteValues = z.infer<typeof employeeInviteSchema>;
37
+ /**
38
+ * The reason recorded against a hard ban.
39
+ *
40
+ * A hard ban runs an eight-stage destructive cascade; this string is the only
41
+ * durable record of why. A 10-character floor, matching the admin offer-cancel
42
+ * reason, because "spam" is not an audit trail.
43
+ */
44
+ export declare const hardBanReasonSchema: z.ZodObject<{
45
+ reason: z.ZodString;
46
+ }, "strip", z.ZodTypeAny, {
47
+ reason: string;
48
+ }, {
49
+ reason: string;
50
+ }>;
51
+ export type HardBanReasonValues = z.infer<typeof hardBanReasonSchema>;
52
+ /**
53
+ * Feature flags.
54
+ *
55
+ * Deliberately open records: flags are added and removed constantly, and a
56
+ * closed schema here would silently STRIP a newly-added flag on the next save
57
+ * — the `productBaseSchema` failure applied to configuration. The values are
58
+ * still constrained, which is what stops a flag being set to an object.
59
+ */
60
+ export declare const featureFlagsUpdateSchema: z.ZodObject<{
61
+ flags: z.ZodRecord<z.ZodString, z.ZodBoolean>;
62
+ rollouts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
63
+ }, "strip", z.ZodTypeAny, {
64
+ flags: Record<string, boolean>;
65
+ rollouts?: Record<string, number> | undefined;
66
+ }, {
67
+ flags: Record<string, boolean>;
68
+ rollouts?: Record<string, number> | undefined;
69
+ }>;
70
+ export type FeatureFlagsUpdateValues = z.infer<typeof featureFlagsUpdateSchema>;
71
+ /** An ad placement. `priority` orders competing ads in the same slot. */
72
+ export declare const adminAdFormSchema: z.ZodEffects<z.ZodObject<{
73
+ name: z.ZodString;
74
+ provider: z.ZodEnum<["manual", "adsense", "thirdParty"]>;
75
+ status: z.ZodEnum<["draft", "active", "scheduled", "paused"]>;
76
+ priority: z.ZodNumber;
77
+ placementIds: z.ZodArray<z.ZodString, "many">;
78
+ requiresConsent: z.ZodBoolean;
79
+ startAt: z.ZodOptional<z.ZodString>;
80
+ endAt: z.ZodOptional<z.ZodString>;
81
+ creative: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("../../..").FirestoreValue, z.ZodTypeDef, import("../../..").FirestoreValue>>>;
82
+ }, "strip", z.ZodTypeAny, {
83
+ status: "draft" | "active" | "scheduled" | "paused";
84
+ name: string;
85
+ priority: number;
86
+ provider: "manual" | "adsense" | "thirdParty";
87
+ placementIds: string[];
88
+ requiresConsent: boolean;
89
+ startAt?: string | undefined;
90
+ endAt?: string | undefined;
91
+ creative?: Record<string, import("../../..").FirestoreValue> | undefined;
92
+ }, {
93
+ status: "draft" | "active" | "scheduled" | "paused";
94
+ name: string;
95
+ priority: number;
96
+ provider: "manual" | "adsense" | "thirdParty";
97
+ placementIds: string[];
98
+ requiresConsent: boolean;
99
+ startAt?: string | undefined;
100
+ endAt?: string | undefined;
101
+ creative?: Record<string, import("../../..").FirestoreValue> | undefined;
102
+ }>, {
103
+ status: "draft" | "active" | "scheduled" | "paused";
104
+ name: string;
105
+ priority: number;
106
+ provider: "manual" | "adsense" | "thirdParty";
107
+ placementIds: string[];
108
+ requiresConsent: boolean;
109
+ startAt?: string | undefined;
110
+ endAt?: string | undefined;
111
+ creative?: Record<string, import("../../..").FirestoreValue> | undefined;
112
+ }, {
113
+ status: "draft" | "active" | "scheduled" | "paused";
114
+ name: string;
115
+ priority: number;
116
+ provider: "manual" | "adsense" | "thirdParty";
117
+ placementIds: string[];
118
+ requiresConsent: boolean;
119
+ startAt?: string | undefined;
120
+ endAt?: string | undefined;
121
+ creative?: Record<string, import("../../..").FirestoreValue> | undefined;
122
+ }>;
123
+ export type AdminAdFormValues = z.infer<typeof adminAdFormSchema>;
@@ -0,0 +1,147 @@
1
+ /*
2
+ * WHY: Four more admin editors rendered `<Form>` with no schema. Each writes
3
+ * something an admin cannot easily undo.
4
+ *
5
+ * `AdminOrderEditorView` is the one that touches money: `refundAmount`
6
+ * was parsed with a bare `parseFloat` and guarded only by
7
+ * `!isNaN(amount) && amount > 0`, with no upper bound and no check
8
+ * against the order — so a mistyped figure went straight through as a
9
+ * refund instruction.
10
+ *
11
+ * `AdminEmployeeEditorView` grants permissions, and `AdminUsersView`'s
12
+ * hard-ban takes a reason that is the entire record of why an account
13
+ * was destroyed.
14
+ *
15
+ * WHAT: One schema per editor.
16
+ *
17
+ * EXPORTS:
18
+ * adminOrderUpdateSchema, employeeInviteSchema, hardBanReasonSchema,
19
+ * featureFlagsUpdateSchema, adminAdFormSchema, and their value types
20
+ *
21
+ * @tag domain:admin
22
+ * @tag layer:schema
23
+ * @tag pattern:none
24
+ * @tag access:isomorphic
25
+ * @tag consumers:AdminOrderEditorView,AdminEmployeeEditorView,AdminUsersView,AdminFeatureFlagsView,AdminAdEditorView
26
+ * @tag sideEffects:none
27
+ */
28
+ import { z } from "zod";
29
+ import { annotate } from "../../shell/field-ui-meta";
30
+ import { firestoreValueSchema } from "../../../schemas/firestore-value";
31
+ import { OrderStatusValues } from "../../orders/schemas/firestore";
32
+ /** Derived from the real 9-value union, never restated (Root Cause #36). */
33
+ const orderStatusSchema = z.enum(Object.values(OrderStatusValues));
34
+ export const adminOrderUpdateSchema = z.object({
35
+ status: annotate(orderStatusSchema, {
36
+ section: "status", sectionLabel: "Order", sectionRequired: true, quick: true, order: 1, row: "pair",
37
+ }),
38
+ trackingNumber: annotate(z.string().trim().max(120).optional(), {
39
+ section: "shipping", sectionLabel: "Shipping", order: 1, row: "pair",
40
+ }),
41
+ carrier: annotate(z.string().trim().max(80).optional(), {
42
+ section: "shipping", order: 2, row: "pair",
43
+ }),
44
+ /*
45
+ * 🛑 Bounded. This was `parseFloat` + `> 0` with no ceiling, so a slipped
46
+ * decimal point issued a refund an order of magnitude too large. The cap is
47
+ * a sanity bound, not a business rule — the route still checks the amount
48
+ * against the order itself.
49
+ */
50
+ refundAmount: annotate(z.coerce
51
+ .number({ invalid_type_error: "Enter the refund amount as a number." })
52
+ .positive("A refund must be greater than zero.")
53
+ .max(10000000, "That looks like a typo — check the decimal point.")
54
+ .optional(), { section: "shipping", order: 3, row: "pair", kind: "number" }),
55
+ notes: annotate(z.string().trim().max(2000).optional(), {
56
+ section: "shipping", order: 4, row: "full", kind: "textarea",
57
+ }),
58
+ });
59
+ /** Inviting a team member — an email plus the permissions it grants. */
60
+ export const employeeInviteSchema = z.object({
61
+ email: annotate(z.string().trim().toLowerCase().min(1, "An email address is required.").email("That doesn't look like an email address."), { section: "member", sectionLabel: "Team member", sectionRequired: true, quick: true, order: 1, row: "pair" }),
62
+ permissionGroup: annotate(z.string().trim().min(1, "Choose a permission group."), {
63
+ section: "member", quick: true, order: 2, row: "pair",
64
+ }),
65
+ permissions: annotate(z.array(z.string().min(1)).max(200).optional(), {
66
+ section: "member", order: 3, row: "full", kind: "list",
67
+ }),
68
+ });
69
+ /**
70
+ * The reason recorded against a hard ban.
71
+ *
72
+ * A hard ban runs an eight-stage destructive cascade; this string is the only
73
+ * durable record of why. A 10-character floor, matching the admin offer-cancel
74
+ * reason, because "spam" is not an audit trail.
75
+ */
76
+ export const hardBanReasonSchema = z.object({
77
+ reason: annotate(z.string().trim().min(10, "Record why this account is being banned — at least 10 characters.").max(500), { section: "ban", sectionLabel: "Reason", sectionRequired: true, quick: true, order: 1, row: "full", kind: "textarea" }),
78
+ });
79
+ /**
80
+ * Feature flags.
81
+ *
82
+ * Deliberately open records: flags are added and removed constantly, and a
83
+ * closed schema here would silently STRIP a newly-added flag on the next save
84
+ * — the `productBaseSchema` failure applied to configuration. The values are
85
+ * still constrained, which is what stops a flag being set to an object.
86
+ */
87
+ export const featureFlagsUpdateSchema = z.object({
88
+ flags: annotate(z.record(z.string(), z.boolean()), {
89
+ section: "flags", sectionLabel: "Feature flags", sectionRequired: true, order: 1, row: "full", kind: "list",
90
+ }),
91
+ rollouts: annotate(z.record(z.string(), z.coerce.number().min(0).max(100)).optional(), {
92
+ section: "flags", order: 2, row: "full", kind: "list",
93
+ help: "Percentage rollout per flag, 0–100.",
94
+ }),
95
+ });
96
+ /** An ad placement. `priority` orders competing ads in the same slot. */
97
+ export const adminAdFormSchema = z
98
+ .object({
99
+ name: annotate(z.string().trim().min(2, "Give the ad a name.").max(120), {
100
+ section: "basics", sectionLabel: "Ad", sectionRequired: true, quick: true, order: 1, row: "pair",
101
+ }),
102
+ provider: annotate(z.enum(["manual", "adsense", "thirdParty"]), {
103
+ section: "basics", quick: true, order: 2, row: "pair",
104
+ }),
105
+ status: annotate(z.enum(["draft", "active", "scheduled", "paused"]), {
106
+ section: "basics", quick: true, order: 3, row: "pair",
107
+ }),
108
+ priority: annotate(z.coerce.number().int().min(0).max(1000), {
109
+ section: "basics", order: 4, row: "pair", kind: "number",
110
+ }),
111
+ placementIds: annotate(z.array(z.string().min(1)).min(1, "Choose at least one placement."), {
112
+ section: "placement", sectionLabel: "Placement", sectionRequired: true, order: 1, row: "full", kind: "list",
113
+ }),
114
+ requiresConsent: annotate(z.boolean(), {
115
+ section: "placement", order: 2, row: "quarter",
116
+ }),
117
+ startAt: annotate(z.string().optional(), {
118
+ section: "schedule", sectionLabel: "Schedule", order: 1, row: "pair", kind: "date",
119
+ }),
120
+ endAt: annotate(z.string().optional(), {
121
+ section: "schedule", order: 2, row: "pair", kind: "date",
122
+ }),
123
+ // Creative shape varies by provider, so it stays open — an AdSense slot and
124
+ // a manual banner share almost no fields. Open to a Firestore VALUE tree,
125
+ // not to anything: `z.unknown()` would pass a function straight to the
126
+ // driver, which then throws at write time rather than at parse time.
127
+ creative: annotate(z.record(firestoreValueSchema).optional(), {
128
+ section: "creative", sectionLabel: "Creative", order: 1, row: "full", kind: "list",
129
+ }),
130
+ })
131
+ .superRefine((v, ctx) => {
132
+ // A scheduled ad with no start date never runs, and nothing would say so.
133
+ if (v.status === "scheduled" && !v.startAt) {
134
+ ctx.addIssue({
135
+ code: z.ZodIssueCode.custom,
136
+ path: ["startAt"],
137
+ message: "A scheduled ad needs a start date.",
138
+ });
139
+ }
140
+ if (v.startAt && v.endAt && v.endAt <= v.startAt) {
141
+ ctx.addIssue({
142
+ code: z.ZodIssueCode.custom,
143
+ path: ["endAt"],
144
+ message: "The end date must be after the start date.",
145
+ });
146
+ }
147
+ });
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Newsletter subscription — one field, shared by all three boxes (the homepage
4
+ * banner, the homepage inline form, and the footer slot).
5
+ *
6
+ * Trimmed and lowercased before validation so ` Foo@Bar.com ` and
7
+ * `foo@bar.com` cannot become two subscriptions for one person.
8
+ */
9
+ export declare const newsletterSubscribeSchema: z.ZodObject<{
10
+ email: z.ZodString;
11
+ }, "strip", z.ZodTypeAny, {
12
+ email: string;
13
+ }, {
14
+ email: string;
15
+ }>;
16
+ export type NewsletterSubscribeValues = z.infer<typeof newsletterSubscribeSchema>;
17
+ /**
18
+ * The quick-create drawer shared in shape by brands and categories.
19
+ *
20
+ * Both drawers post `{ name, description, isActive }` to their own endpoint
21
+ * and differ in nothing else, so one schema serves both. `slug` is absent
22
+ * deliberately — it is derived from the name on the server, and this codebase
23
+ * treats a slug as create-only and non-editable everywhere else.
24
+ */
25
+ export declare const quickCreateTaxonomySchema: z.ZodObject<{
26
+ name: z.ZodString;
27
+ description: z.ZodOptional<z.ZodString>;
28
+ isActive: z.ZodOptional<z.ZodBoolean>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ name: string;
31
+ description?: string | undefined;
32
+ isActive?: boolean | undefined;
33
+ }, {
34
+ name: string;
35
+ description?: string | undefined;
36
+ isActive?: boolean | undefined;
37
+ }>;
38
+ export type QuickCreateTaxonomyValues = z.infer<typeof quickCreateTaxonomySchema>;
39
+ /**
40
+ * Marking a payout paid.
41
+ *
42
+ * `transactionId` is REQUIRED, which it was not before. This is the record
43
+ * that money left the building — an empty one leaves a payout marked paid with
44
+ * nothing to reconcile it against, and no way to answer a seller asking where
45
+ * their money went.
46
+ */
47
+ export declare const payoutMarkPaidSchema: z.ZodObject<{
48
+ transactionId: z.ZodString;
49
+ }, "strip", z.ZodTypeAny, {
50
+ transactionId: string;
51
+ }, {
52
+ transactionId: string;
53
+ }>;
54
+ export type PayoutMarkPaidValues = z.infer<typeof payoutMarkPaidSchema>;
@@ -0,0 +1,85 @@
1
+ /*
2
+ * WHY: A cluster of small forms — three newsletter boxes, two quick-create
3
+ * drawers, and the payout mark-paid modal — each rendered `<Form>` with
4
+ * no schema. Individually each is two or three fields; together they are
5
+ * six surfaces where the only validation was whatever the server said
6
+ * after the round-trip, reported as a toast.
7
+ *
8
+ * The newsletter trio matters most despite being one field: an email box
9
+ * that accepts anything sends a subscription request for `"asdf"` and
10
+ * shows a success message.
11
+ *
12
+ * WHAT: One module for the small shared shapes, rather than six schema files
13
+ * of three lines each.
14
+ *
15
+ * ## Why these live together and the bigger ones do not
16
+ *
17
+ * These have no per-entity semantics worth a file of their own — an email is
18
+ * an email. An entity with real rules (a payout method's per-type bank
19
+ * requirements, a listing template's defaults tree) keeps its own module,
20
+ * because that is where the reasoning belongs. Grouping by *size* rather than
21
+ * by domain is the exception here, and it stops at the point where a schema
22
+ * needs a paragraph to explain itself.
23
+ *
24
+ * EXPORTS:
25
+ * newsletterSubscribeSchema, quickCreateTaxonomySchema,
26
+ * payoutMarkPaidSchema, and their inferred value types
27
+ *
28
+ * @tag domain:admin,homepage,payments
29
+ * @tag layer:schema
30
+ * @tag pattern:none
31
+ * @tag access:isomorphic
32
+ * @tag consumers:newsletter forms,quick-create drawers,AdminPayoutMarkPaidModal
33
+ * @tag sideEffects:none
34
+ */
35
+ import { z } from "zod";
36
+ import { annotate } from "../../shell/field-ui-meta";
37
+ /**
38
+ * Newsletter subscription — one field, shared by all three boxes (the homepage
39
+ * banner, the homepage inline form, and the footer slot).
40
+ *
41
+ * Trimmed and lowercased before validation so ` Foo@Bar.com ` and
42
+ * `foo@bar.com` cannot become two subscriptions for one person.
43
+ */
44
+ export const newsletterSubscribeSchema = z.object({
45
+ email: annotate(z
46
+ .string()
47
+ .trim()
48
+ .toLowerCase()
49
+ .min(1, "Enter your email address.")
50
+ .email("That doesn't look like an email address."), { section: "subscribe", sectionLabel: "Newsletter", sectionRequired: true, quick: true, order: 1, row: "full" }),
51
+ });
52
+ /**
53
+ * The quick-create drawer shared in shape by brands and categories.
54
+ *
55
+ * Both drawers post `{ name, description, isActive }` to their own endpoint
56
+ * and differ in nothing else, so one schema serves both. `slug` is absent
57
+ * deliberately — it is derived from the name on the server, and this codebase
58
+ * treats a slug as create-only and non-editable everywhere else.
59
+ */
60
+ export const quickCreateTaxonomySchema = z.object({
61
+ name: annotate(z.string().trim().min(1, "A name is required.").max(80, "Keep the name under 80 characters."), {
62
+ section: "basics", sectionLabel: "Details", sectionRequired: true, quick: true, order: 1, row: "full",
63
+ }),
64
+ description: annotate(z.string().max(500, "Keep the description under 500 characters.").optional(), {
65
+ section: "basics", order: 2, row: "full", kind: "textarea",
66
+ }),
67
+ isActive: annotate(z.boolean().optional(), {
68
+ section: "basics", order: 3, row: "quarter",
69
+ }),
70
+ });
71
+ /**
72
+ * Marking a payout paid.
73
+ *
74
+ * `transactionId` is REQUIRED, which it was not before. This is the record
75
+ * that money left the building — an empty one leaves a payout marked paid with
76
+ * nothing to reconcile it against, and no way to answer a seller asking where
77
+ * their money went.
78
+ */
79
+ export const payoutMarkPaidSchema = z.object({
80
+ transactionId: annotate(z
81
+ .string()
82
+ .trim()
83
+ .min(3, "Enter the bank or UPI reference for this transfer.")
84
+ .max(120, "Keep the reference under 120 characters."), { section: "payment", sectionLabel: "Payment", sectionRequired: true, quick: true, order: 1, row: "full" }),
85
+ });
@@ -7,6 +7,7 @@ import { Form } from "../../../ui/components/Form";
7
7
  import { FieldInput } from "../../../ui/forms/FieldInput";
8
8
  import { applyZodIssues } from "../../../ui/forms/FormShell";
9
9
  import { forgotPasswordSchema } from "../schemas";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
10
11
  export function ForgotPasswordView({ onSubmit, isLoading = false, error, success, labels = {}, renderBackLink, className = "", }) {
11
12
  const [email, setEmail] = useState("");
12
13
  const { showToast } = useToast();
@@ -26,7 +27,7 @@ export function ForgotPasswordView({ onSubmit, isLoading = false, error, success
26
27
  };
27
28
  }
28
29
  return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Forgot Password" }), _jsx(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: labels.description ??
29
- "Enter your email and we'll send you a reset link." })] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), !success && (_jsx(Form, { noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: email, onChange: setEmail }), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: buildClickHandler(setFieldError, clearErrors), children: isLoading
30
+ "Enter your email and we'll send you a reset link." })] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), !success && (_jsx(Form, { schema: forgotPasswordSchema, noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: email, onChange: setEmail }), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: buildClickHandler(setFieldError, clearErrors), children: isLoading
30
31
  ? (labels.submittingLabel ?? "Sending…")
31
32
  : (labels.submitLabel ?? "Send reset link") })] })) })), renderBackLink && (_jsx(Div, { textSize: "sm", className: "text-center", children: renderBackLink() }))] }) }));
32
33
  }
@@ -9,6 +9,7 @@ import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
9
9
  import { applyZodIssues } from "../../../ui/forms/FormShell";
10
10
  import { loginSchema } from "../schemas";
11
11
  import { SocialAuthButtons } from "./SocialAuthButtons";
12
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
12
13
  export function LoginForm({ onSubmit, onGoogleLogin, isLoading = false, error, labels = {}, renderSocialButtons, renderCreateAccountLink, renderForgotPasswordLink, className = "", }) {
13
14
  const { showToast } = useToast();
14
15
  const [values, setValues] = useState({
@@ -16,7 +17,7 @@ export function LoginForm({ onSubmit, onGoogleLogin, isLoading = false, error, l
16
17
  password: "",
17
18
  rememberMe: false,
18
19
  });
19
- return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Sign In" }), (labels.subtitle || renderCreateAccountLink) && (_jsxs(Text, { className: "mt-2", color: "muted", size: "sm", children: [labels.subtitle ?? "Or", " ", renderCreateAccountLink?.()] }))] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsx(Form, { noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => setValues({ ...values, email: v }) }), _jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "current-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => setValues({ ...values, password: v }) }), _jsxs(Row, { justify: "between", children: [_jsx(FieldCheckbox, { name: "rememberMe", label: labels.rememberMe ?? "Remember me", checked: values.rememberMe ?? false, onChange: (c) => setValues({ ...values, rememberMe: c }) }), renderForgotPasswordLink?.()] }), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: async () => {
20
+ return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Sign In" }), (labels.subtitle || renderCreateAccountLink) && (_jsxs(Text, { className: "mt-2", color: "muted", size: "sm", children: [labels.subtitle ?? "Or", " ", renderCreateAccountLink?.()] }))] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsx(Form, { schema: loginSchema, noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => setValues({ ...values, email: v }) }), _jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "current-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => setValues({ ...values, password: v }) }), _jsxs(Row, { justify: "between", children: [_jsx(FieldCheckbox, { name: "rememberMe", label: labels.rememberMe ?? "Remember me", checked: values.rememberMe ?? false, onChange: (c) => setValues({ ...values, rememberMe: c }) }), renderForgotPasswordLink?.()] }), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: async () => {
20
21
  clearErrors();
21
22
  const parsed = loginSchema.safeParse(values);
22
23
  if (!parsed.success)
@@ -9,6 +9,7 @@ import { FieldInput } from "../../../ui/forms/FieldInput";
9
9
  import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
10
10
  import { applyZodIssues } from "../../../ui/forms/FormShell";
11
11
  import { registerSchema, registerPasswordSchema } from "../schemas";
12
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
12
13
  const registerClientSchema = registerSchema.extend({
13
14
  displayName: z.string().min(1, "Enter your name"),
14
15
  confirmPassword: registerPasswordSchema,
@@ -28,7 +29,7 @@ export function RegisterForm({ onSubmit, isLoading = false, error, success, labe
28
29
  displayName: "",
29
30
  acceptTerms: false,
30
31
  });
31
- return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsx(Form, { noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "displayName", label: labels.displayNameLabel ?? "Full name", type: "text", autoComplete: "name", required: true, placeholder: labels.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (v) => setValues({ ...values, displayName: v }) }), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => setValues({ ...values, email: v }) }), _jsxs(Div, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => setValues({ ...values, password: v }) }), renderPasswordStrength?.(values.password)] }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (v) => setValues({ ...values, confirmPassword: v }) }), _jsx(FieldCheckbox, { name: "acceptTerms", label: `${labels.acceptTermsLabel ?? "I accept the terms"}`, checked: values.acceptTerms, onChange: (c) => setValues({ ...values, acceptTerms: c }) }), renderTermsLink && (_jsx(Text, { size: "xs", variant: "secondary", children: renderTermsLink() })), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: async () => {
32
+ return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsx(Form, { schema: registerClientSchema, noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "displayName", label: labels.displayNameLabel ?? "Full name", type: "text", autoComplete: "name", required: true, placeholder: labels.displayNamePlaceholder ?? "Your name", value: values.displayName, onChange: (v) => setValues({ ...values, displayName: v }) }), _jsx(FieldInput, { name: "email", label: labels.emailLabel ?? "Email address", type: "email", autoComplete: "username", required: true, placeholder: labels.emailPlaceholder ?? "you@example.com", value: values.email, onChange: (v) => setValues({ ...values, email: v }) }), _jsxs(Div, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "Password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: values.password, onChange: (v) => setValues({ ...values, password: v }) }), renderPasswordStrength?.(values.password)] }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: values.confirmPassword, onChange: (v) => setValues({ ...values, confirmPassword: v }) }), _jsx(FieldCheckbox, { name: "acceptTerms", label: `${labels.acceptTermsLabel ?? "I accept the terms"}`, checked: values.acceptTerms, onChange: (c) => setValues({ ...values, acceptTerms: c }) }), renderTermsLink && (_jsx(Text, { size: "xs", variant: "secondary", children: renderTermsLink() })), _jsx(Button, { type: "submit", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: async () => {
32
33
  clearErrors();
33
34
  const parsed = registerClientSchema.safeParse(values);
34
35
  if (!parsed.success)
@@ -7,6 +7,7 @@ import { Alert, Button, Div, Heading, Row, Stack, Text, useToast } from "../../.
7
7
  import { Form } from "../../../ui/components/Form";
8
8
  import { FieldInput } from "../../../ui/forms/FieldInput";
9
9
  import { applyZodIssues } from "../../../ui/forms/FormShell";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
10
11
  const resetClientSchema = z.object({
11
12
  password: z.string().min(6, "Password must be at least 6 characters"),
12
13
  confirmPassword: z.string().min(6),
@@ -33,7 +34,7 @@ export function ResetPasswordView({ oobCode, onSubmit, isLoading = false, error,
33
34
  }
34
35
  };
35
36
  }
36
- return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Reset Password" }), labels.description && (_jsx(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: labels.description }))] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), success ? (_jsxs(Alert, { variant: "success", compact: true, children: [success, renderLoginLink && _jsx(Div, { className: "mt-2", children: renderLoginLink() })] })) : (_jsx(Form, { noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "New password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: password, onChange: setPassword }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm new password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: confirmPassword, onChange: setConfirmPassword }), _jsx(Button, { type: "submit", variant: "primary", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: buildClickHandler(setFieldError, clearErrors), children: isLoading
37
+ return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Reset Password" }), labels.description && (_jsx(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: labels.description }))] }), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), success ? (_jsxs(Alert, { variant: "success", compact: true, children: [success, renderLoginLink && _jsx(Div, { className: "mt-2", children: renderLoginLink() })] })) : (_jsx(Form, { schema: resetClientSchema, noValidate: true, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "password", label: labels.passwordLabel ?? "New password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.passwordPlaceholder ?? "••••••••", value: password, onChange: setPassword }), _jsx(FieldInput, { name: "confirmPassword", label: labels.confirmPasswordLabel ?? "Confirm new password", type: "password", autoComplete: "new-password", required: true, placeholder: labels.confirmPasswordPlaceholder ?? "••••••••", value: confirmPassword, onChange: setConfirmPassword }), _jsx(Button, { type: "submit", variant: "primary", isLoading: isLoading, disabled: isLoading, className: "w-full", onClick: buildClickHandler(setFieldError, clearErrors), children: isLoading
37
38
  ? (labels.submittingLabel ?? "Resetting…")
38
39
  : (labels.submitLabel ?? "Reset password") })] })) }))] }) }));
39
40
  }