@mohasinac/appkit 4.16.1 → 4.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -434,3 +434,5 @@ export { SELLER_LISTING_TABS, type SellerListingTabId, } from "./features/produc
434
434
  export { ALL_TAB, EMPTY_TAB, ADMIN_PRODUCT_STATUS_TABS, ADMIN_PRODUCT_LISTING_TYPE_TABS, ADMIN_BLOG_STATUS_TABS, ADMIN_USER_STATUS_TABS, ADMIN_USER_ROLE_TABS, ADMIN_STORE_STATUS_TABS, ADMIN_PAYOUT_STATUS_TABS, ADMIN_ORDER_STATUS_TABS, ADMIN_REVIEW_STATUS_TABS, ADMIN_REVIEW_RATING_TABS, ADMIN_BID_STATUS_TABS, ADMIN_CONTACT_STATUS_TABS, ADMIN_NEWSLETTER_STATUS_TABS, ADMIN_EVENT_ENTRY_STATUS_TABS, ADMIN_EVENT_STATUS_TABS, ADMIN_CART_OWNERSHIP_TABS, ADMIN_COUPON_TYPE_TABS, SELLER_PRODUCT_STATUS_TABS, SELLER_AUCTION_STATUS_TABS, SELLER_ORDER_STATUS_TABS, SELLER_OFFER_STATUS_TABS, SELLER_BID_STATUS_TABS, } from "./features/admin/constants/filter-tabs";
435
435
  export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS, BID_FIELDS, AD_FIELDS, EVENT_FIELDS, EVENT_ENTRY_FIELDS, PAYOUT_FIELDS, STORE_FIELDS, CATEGORY_FIELDS, BLOG_FIELDS, USER_FIELDS, ADDRESS_FIELDS, BRAND_FIELDS, CART_FIELDS, WISHLIST_FIELDS, HISTORY_FIELDS, NOTIFICATION_FIELDS, SESSION_FIELDS, COUPON_USAGE_FIELDS, CONVERSATION_FIELDS, SCAMMER_FIELDS, SUPPORT_TICKET_FIELDS, CAROUSEL_FIELDS, COUPON_FIELDS, FAQ_FIELDS, HOMEPAGE_SECTION_FIELDS, SITE_SETTINGS_FIELDS, COMMON_FIELDS, OAUTH_STATE_VALUES, SCHEMA_DEFAULTS, } from "./constants/field-names";
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
+ export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
438
+ export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
package/dist/client.js CHANGED
@@ -420,3 +420,5 @@ export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS
420
420
  // Checkout lanes — the derived auction > offer > standard partition of the
421
421
  // cart, and the priority rule that decides which one may be checked out.
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
+ export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
424
+ export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  /**
4
4
  * AdminPayoutMarkPaidModal — the single "mark payout as paid" form + mutation,
5
5
  * shared by the payouts list row action and the standalone payout detail
@@ -9,9 +9,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  import { useState } from "react";
10
10
  import { useApiMutation } from "@mohasinac/appkit/client";
11
11
  import { useQueryClient } from "@tanstack/react-query";
12
- import { Button, Form, FormActions, Input, Modal, useToast } from "../../../ui";
12
+ import { Button, Form, FormActions, Modal, useToast } from "../../../ui";
13
13
  import { apiClient } from "../../../http";
14
14
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
15
+ import { payoutMarkPaidSchema } from "../schemas/small-forms";
16
+ import { FieldInput } from "../../../ui/forms/FieldInput";
17
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
18
+ import { applyZodIssues } from "../../../ui/forms/FormShell";
15
19
  export function AdminPayoutMarkPaidModal({ isOpen, payoutId, onClose, onSuccess }) {
16
20
  const [transactionId, setTransactionId] = useState("");
17
21
  const { showToast } = useToast();
@@ -40,8 +44,16 @@ export function AdminPayoutMarkPaidModal({ isOpen, payoutId, onClose, onSuccess
40
44
  setTransactionId("");
41
45
  onClose();
42
46
  };
43
- return (_jsx(Modal, { isOpen: isOpen, onClose: handleClose, title: "Mark payout as paid", children: _jsxs(Form, { onSubmit: (e) => {
44
- e.preventDefault();
45
- markPaid.mutate();
46
- }, spacing: "md", children: [_jsx(Input, { label: "Transaction / reference ID", value: transactionId, onChange: (e) => setTransactionId(e.target.value), placeholder: "UTR, UPI ref, or bank transfer ID (optional)" }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: handleClose, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: markPaid.isPending, children: markPaid.isPending ? "Saving..." : "Confirm paid" })] })] }) }));
47
+ return (_jsx(Modal, { isOpen: isOpen, onClose: handleClose, title: "Mark payout as paid", children: _jsx(Form, { schema: payoutMarkPaidSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "transactionId", label: "Transaction / reference ID", required: true, value: transactionId, onChange: setTransactionId, placeholder: "UTR, UPI ref, or bank transfer ID" }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: handleClose, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: markPaid.isPending, onClick: () => {
48
+ clearErrors();
49
+ // The reference used to be optional and its placeholder even
50
+ // said so — which left payouts marked paid with nothing to
51
+ // reconcile them against.
52
+ const parsed = payoutMarkPaidSchema.safeParse({ transactionId });
53
+ if (!parsed.success) {
54
+ applyZodIssues(parsed.error.issues, setFieldError);
55
+ return;
56
+ }
57
+ markPaid.mutate();
58
+ }, children: markPaid.isPending ? "Saving..." : "Confirm paid" })] })] })) }) }));
47
59
  }
@@ -1,11 +1,14 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Row } from "@mohasinac/appkit/ui";
4
4
  import { useApiMutation } from "@mohasinac/appkit/client";
5
5
  import React from "react";
6
6
  import { Button, Form, Input, Toggle, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { quickCreateTaxonomySchema } from "../schemas/small-forms";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
11
+ import { applyZodIssues } from "../../../ui/forms/FormShell";
9
12
  function toBrandSlug(str) {
10
13
  const base = str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11
14
  return base.startsWith("brand-") ? base : `brand-${base}`;
@@ -30,8 +33,13 @@ export function BrandQuickCreateForm({ onSaved, onCancel }) {
30
33
  showToast(err?.message ?? "Failed to create brand.", "error");
31
34
  },
32
35
  });
33
- return (_jsxs(Form, { onSubmit: (e) => {
34
- e.preventDefault();
35
- mutation.mutate();
36
- }, spacing: "md", children: [_jsx(Input, { label: "Brand name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Hot Wheels", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: !name || mutation.isPending, children: "Create brand" }), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, children: "Cancel" })] })] }));
36
+ return (_jsx(Form, { schema: quickCreateTaxonomySchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Brand name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Hot Wheels", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: mutation.isPending, onClick: () => {
37
+ clearErrors();
38
+ const parsed = quickCreateTaxonomySchema.safeParse({ name, description, isActive });
39
+ if (!parsed.success) {
40
+ applyZodIssues(parsed.error.issues, setFieldError);
41
+ return;
42
+ }
43
+ mutation.mutate();
44
+ }, children: "Create brand" }), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, children: "Cancel" })] })] })) }));
37
45
  }
@@ -1,11 +1,14 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { Row } from "@mohasinac/appkit/ui";
4
4
  import { useApiMutation } from "@mohasinac/appkit/client";
5
5
  import React from "react";
6
6
  import { Button, Form, Input, Toggle, useToast } from "../../../ui";
7
7
  import { apiClient } from "../../../http";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { quickCreateTaxonomySchema } from "../schemas/small-forms";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
11
+ import { applyZodIssues } from "../../../ui/forms/FormShell";
9
12
  function toCategorySlug(str) {
10
13
  return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11
14
  }
@@ -29,8 +32,13 @@ export function CategoryQuickCreateForm({ onSaved, onCancel }) {
29
32
  showToast(err?.message ?? "Failed to create category.", "error");
30
33
  },
31
34
  });
32
- return (_jsxs(Form, { onSubmit: (e) => {
33
- e.preventDefault();
34
- mutation.mutate();
35
- }, spacing: "md", children: [_jsx(Input, { label: "Category name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Trading Cards", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: !name || mutation.isPending, children: "Create category" }), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, children: "Cancel" })] })] }));
35
+ return (_jsx(Form, { schema: quickCreateTaxonomySchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsx(FormErrorSummary, {}), _jsx(Input, { label: "Category name", value: name, onChange: (e) => setName(e.target.value), required: true, placeholder: "e.g. Trading Cards", autoFocus: true }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Optional brief description" }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: mutation.isPending, disabled: mutation.isPending, onClick: () => {
36
+ clearErrors();
37
+ const parsed = quickCreateTaxonomySchema.safeParse({ name, description, isActive });
38
+ if (!parsed.success) {
39
+ applyZodIssues(parsed.error.issues, setFieldError);
40
+ return;
41
+ }
42
+ mutation.mutate();
43
+ }, children: "Create category" }), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, children: "Cancel" })] })] })) }));
36
44
  }
@@ -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
  }
@@ -5,6 +5,8 @@ import { Button, Row, Select } from "../../../ui";
5
5
  import { Form } from "../../../ui/components/Form";
6
6
  import { FieldInput } from "../../../ui/forms/FieldInput";
7
7
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
8
+ import { bookConsultationSchema } from "../schemas";
9
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
8
10
  const TIME_SLOTS = [
9
11
  "9:00 AM",
10
12
  "10:00 AM",
@@ -41,7 +43,7 @@ export function ConsultationForm({ onSubmit, isPending, concerns = [], }) {
41
43
  e.preventDefault();
42
44
  await onSubmit(form);
43
45
  }
44
- return (_jsxs(Form, { onSubmit: handleSubmit, spacing: "md", children: [_jsx(FieldInput, { name: "name", type: "text", placeholder: "Full name", value: form.name, onChange: (v) => handleValueChange("name", v), required: true }), _jsx(FieldInput, { name: "email", type: "email", placeholder: "Email address", value: form.email, onChange: (v) => handleValueChange("email", v), required: true }), _jsx(FieldInput, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: (v) => handleValueChange("phone", v), required: true }), concerns.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: concerns.map((c) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => toggleConcern(c), className: `rounded-full border px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] transition ${form.concern.includes(c)
46
+ return (_jsxs(Form, { schema: bookConsultationSchema, onSubmit: handleSubmit, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "name", type: "text", placeholder: "Full name", value: form.name, onChange: (v) => handleValueChange("name", v), required: true }), _jsx(FieldInput, { name: "email", type: "email", placeholder: "Email address", value: form.email, onChange: (v) => handleValueChange("email", v), required: true }), _jsx(FieldInput, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: (v) => handleValueChange("phone", v), required: true }), concerns.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: concerns.map((c) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => toggleConcern(c), className: `rounded-full border px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] transition ${form.concern.includes(c)
45
47
  ? "border-primary bg-primary text-white"
46
48
  : "border-neutral-300 text-neutral-700 hover:border-primary"}`, children: c }, c))) })), _jsxs(Row, { gap: "3", children: [_jsx(FieldInput, { name: "preferredDate", type: "date", value: form.preferredDate, onChange: (v) => handleValueChange("preferredDate", v), required: true, className: "flex-1" }), _jsx(Select, { value: form.preferredTime, onValueChange: (value) => handleValueChange("preferredTime", value), options: TIME_SLOTS.map((t) => ({ value: t, label: t })), wrapperClassName: "flex-1" })] }), _jsx(Select, { value: form.mode, onValueChange: (value) => handleValueChange("mode", value), options: [
47
49
  { value: "remote", label: "Remote (Video Call)" },
@@ -60,8 +60,8 @@ export declare const consultationBookingSchema: z.ZodObject<{
60
60
  export declare const bookConsultationSchema: z.ZodObject<{
61
61
  name: z.ZodString;
62
62
  email: z.ZodString;
63
- phone: z.ZodOptional<z.ZodString>;
64
- concern: z.ZodArray<z.ZodString, "many">;
63
+ phone: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
64
+ concern: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
65
65
  preferredDate: z.ZodOptional<z.ZodString>;
66
66
  preferredTime: z.ZodOptional<z.ZodString>;
67
67
  mode: z.ZodOptional<z.ZodEnum<["online", "in_person", "phone"]>>;
@@ -69,19 +69,19 @@ export declare const bookConsultationSchema: z.ZodObject<{
69
69
  }, "strip", z.ZodTypeAny, {
70
70
  email: string;
71
71
  name: string;
72
- concern: string[];
73
72
  message?: string | undefined;
74
73
  phone?: string | undefined;
75
74
  mode?: "phone" | "online" | "in_person" | undefined;
75
+ concern?: string[] | undefined;
76
76
  preferredDate?: string | undefined;
77
77
  preferredTime?: string | undefined;
78
78
  }, {
79
79
  email: string;
80
80
  name: string;
81
- concern: string[];
82
81
  message?: string | undefined;
83
82
  phone?: string | undefined;
84
83
  mode?: "phone" | "online" | "in_person" | undefined;
84
+ concern?: string[] | undefined;
85
85
  preferredDate?: string | undefined;
86
86
  preferredTime?: string | undefined;
87
87
  }>;
@@ -36,12 +36,29 @@ export const consultationBookingSchema = z.object({
36
36
  * Form schema for submitting a new consultation booking.
37
37
  */
38
38
  export const bookConsultationSchema = z.object({
39
- name: z.string().min(2),
40
- email: z.string().email(),
41
- phone: z.string().optional(),
42
- concern: z.array(z.string()).min(1, "Select at least one concern"),
39
+ name: z.string().trim().min(2, "Please tell us your name.").max(120),
40
+ email: z
41
+ .string()
42
+ .trim()
43
+ .toLowerCase()
44
+ .min(1, "An email address is required.")
45
+ .email("That doesn't look like an email address."),
46
+ // Same shape as the corporate enquiry's — see the note there.
47
+ phone: z
48
+ .string()
49
+ .trim()
50
+ .regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
51
+ .optional()
52
+ .or(z.literal("")),
53
+ /*
54
+ * Optional here, though the document treats it as the subject of the
55
+ * booking: the public form does not render a concern picker, so requiring it
56
+ * would reject every submission the form can produce. The admin-side booking
57
+ * path is where it is set.
58
+ */
59
+ concern: z.array(z.string()).optional(),
43
60
  preferredDate: z.string().optional(),
44
61
  preferredTime: z.string().optional(),
45
62
  mode: consultationModeSchema.optional(),
46
- message: z.string().optional(),
63
+ message: z.string().trim().max(4000).optional(),
47
64
  });
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * A raffle entry is one optional message — the entry itself is the act of
4
+ * submitting. Bounded because the text is rendered back on a leaderboard.
5
+ */
6
+ export declare const raffleEntrySchema: z.ZodObject<{
7
+ message: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ message?: string | undefined;
10
+ }, {
11
+ message?: string | undefined;
12
+ }>;
13
+ export type RaffleEntryValues = z.infer<typeof raffleEntrySchema>;
@@ -0,0 +1,34 @@
1
+ /*
2
+ * WHY: `EventRaffleEntryForm` rendered `<Form>` with no schema, so a raffle
3
+ * entry's message was unbounded — and it is rendered back on a public
4
+ * leaderboard.
5
+ *
6
+ * ## Why this file holds ONE schema and not three
7
+ *
8
+ * It was drafted with three: consultation, corporate and raffle. Two of those
9
+ * were already implemented — `bookConsultationSchema` and
10
+ * `submitCorporateInquirySchema` have existed in their own features all along,
11
+ * and their forms simply never used them. Adding a second copy of each would
12
+ * have been precisely the duplication this rework exists to remove, so those
13
+ * two were improved in place instead and only the raffle schema remains here.
14
+ *
15
+ * EXPORTS: raffleEntrySchema, type RaffleEntryValues
16
+ *
17
+ * @tag domain:events
18
+ * @tag layer:schema
19
+ * @tag pattern:none
20
+ * @tag access:isomorphic
21
+ * @tag consumers:EventRaffleEntryForm
22
+ * @tag sideEffects:none
23
+ */
24
+ import { z } from "zod";
25
+ import { annotate } from "../../shell/field-ui-meta";
26
+ /**
27
+ * A raffle entry is one optional message — the entry itself is the act of
28
+ * submitting. Bounded because the text is rendered back on a leaderboard.
29
+ */
30
+ export const raffleEntrySchema = z.object({
31
+ message: annotate(z.string().trim().max(500, "Keep it under 500 characters.").optional(), {
32
+ section: "entry", sectionLabel: "Your entry", sectionRequired: true, quick: true, order: 1, row: "full", kind: "textarea",
33
+ }),
34
+ });
@@ -6,6 +6,8 @@ import { Form } from "../../../ui/components/Form";
6
6
  import { FieldInput } from "../../../ui/forms/FieldInput";
7
7
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
8
8
  import { FieldCheckbox } from "../../../ui/forms/FieldCheckbox";
9
+ import { submitCorporateInquirySchema } from "../schemas";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
9
11
  export function CorporateInquiryForm({ onSubmit, isPending, }) {
10
12
  const [form, setForm] = React.useState({
11
13
  companyName: "",
@@ -26,5 +28,5 @@ export function CorporateInquiryForm({ onSubmit, isPending, }) {
26
28
  e.preventDefault();
27
29
  await onSubmit(form);
28
30
  }
29
- return (_jsxs(Form, { onSubmit: handleSubmit, spacing: "md", children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-1 sm:grid-cols-2", children: [_jsx(FieldInput, { name: "companyName", type: "text", placeholder: "Company name", value: form.companyName, onChange: (v) => patch("companyName", v), required: true }), _jsx(FieldInput, { name: "contactPerson", type: "text", placeholder: "Contact person", value: form.contactPerson, onChange: (v) => patch("contactPerson", v), required: true }), _jsx(FieldInput, { name: "designation", type: "text", placeholder: "Designation (optional)", value: form.designation ?? "", onChange: (v) => patch("designation", v) }), _jsx(FieldInput, { name: "email", type: "email", placeholder: "Business email", value: form.email, onChange: (v) => patch("email", v), required: true }), _jsx(FieldInput, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: (v) => patch("phone", v), required: true }), _jsx(FieldInput, { name: "units", type: "number", placeholder: "Number of units", value: form.units, onChange: (v) => patch("units", v === "" ? 0 : Number(v)), min: 1, required: true }), _jsx(FieldInput, { name: "budgetPerUnit", type: "number", placeholder: "Budget per unit (optional)", value: form.budgetPerUnit ?? "", onChange: (v) => patch("budgetPerUnit", v === "" ? undefined : Number(v)), min: 0 }), _jsx(FieldInput, { name: "deliveryDateRequired", type: "date", placeholder: "Required by (optional)", value: form.deliveryDateRequired ?? "", onChange: (v) => patch("deliveryDateRequired", v) })] }), _jsx(FieldCheckbox, { name: "customBranding", label: "Custom branding required", checked: form.customBranding, onChange: (c) => patch("customBranding", c) }), _jsx(FieldTextarea, { name: "message", placeholder: "Additional requirements (optional)", value: form.message ?? "", onChange: (v) => patch("message", v), rows: 4 }), _jsx(Button, { type: "submit", isLoading: isPending, disabled: isPending, variant: "primary", className: "w-full", children: isPending ? "Submitting..." : "Submit Inquiry" }), _jsx(Text, { size: "xs", variant: "secondary", children: "We'll respond within 1-2 business days." })] }));
31
+ return (_jsxs(Form, { schema: submitCorporateInquirySchema, onSubmit: handleSubmit, spacing: "md", children: [_jsx(FormErrorSummary, {}), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-1 sm:grid-cols-2", children: [_jsx(FieldInput, { name: "companyName", type: "text", placeholder: "Company name", value: form.companyName, onChange: (v) => patch("companyName", v), required: true }), _jsx(FieldInput, { name: "contactPerson", type: "text", placeholder: "Contact person", value: form.contactPerson, onChange: (v) => patch("contactPerson", v), required: true }), _jsx(FieldInput, { name: "designation", type: "text", placeholder: "Designation (optional)", value: form.designation ?? "", onChange: (v) => patch("designation", v) }), _jsx(FieldInput, { name: "email", type: "email", placeholder: "Business email", value: form.email, onChange: (v) => patch("email", v), required: true }), _jsx(FieldInput, { name: "phone", type: "tel", placeholder: "Phone number", value: form.phone, onChange: (v) => patch("phone", v), required: true }), _jsx(FieldInput, { name: "units", type: "number", placeholder: "Number of units", value: form.units, onChange: (v) => patch("units", v === "" ? 0 : Number(v)), min: 1, required: true }), _jsx(FieldInput, { name: "budgetPerUnit", type: "number", placeholder: "Budget per unit (optional)", value: form.budgetPerUnit ?? "", onChange: (v) => patch("budgetPerUnit", v === "" ? undefined : Number(v)), min: 0 }), _jsx(FieldInput, { name: "deliveryDateRequired", type: "date", placeholder: "Required by (optional)", value: form.deliveryDateRequired ?? "", onChange: (v) => patch("deliveryDateRequired", v) })] }), _jsx(FieldCheckbox, { name: "customBranding", label: "Custom branding required", checked: form.customBranding, onChange: (c) => patch("customBranding", c) }), _jsx(FieldTextarea, { name: "message", placeholder: "Additional requirements (optional)", value: form.message ?? "", onChange: (v) => patch("message", v), rows: 4 }), _jsx(Button, { type: "submit", isLoading: isPending, disabled: isPending, variant: "primary", className: "w-full", children: isPending ? "Submitting..." : "Submit Inquiry" }), _jsx(Text, { size: "xs", variant: "secondary", children: "We'll respond within 1-2 business days." })] }));
30
32
  }
@@ -64,17 +64,19 @@ export declare const submitCorporateInquirySchema: z.ZodObject<{
64
64
  contactPerson: z.ZodString;
65
65
  designation: z.ZodOptional<z.ZodString>;
66
66
  email: z.ZodString;
67
- phone: z.ZodOptional<z.ZodString>;
67
+ phone: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
68
68
  units: z.ZodOptional<z.ZodNumber>;
69
69
  budgetPerUnit: z.ZodOptional<z.ZodNumber>;
70
70
  totalBudget: z.ZodOptional<z.ZodNumber>;
71
71
  deliveryDateRequired: z.ZodOptional<z.ZodString>;
72
72
  customBranding: z.ZodDefault<z.ZodBoolean>;
73
+ message: z.ZodOptional<z.ZodString>;
73
74
  }, "strip", z.ZodTypeAny, {
74
75
  email: string;
75
76
  companyName: string;
76
77
  contactPerson: string;
77
78
  customBranding: boolean;
79
+ message?: string | undefined;
78
80
  phone?: string | undefined;
79
81
  designation?: string | undefined;
80
82
  units?: number | undefined;
@@ -85,6 +87,7 @@ export declare const submitCorporateInquirySchema: z.ZodObject<{
85
87
  email: string;
86
88
  companyName: string;
87
89
  contactPerson: string;
90
+ message?: string | undefined;
88
91
  phone?: string | undefined;
89
92
  designation?: string | undefined;
90
93
  units?: number | undefined;
@@ -37,14 +37,44 @@ export const corporateInquirySchema = z.object({
37
37
  * Form schema for submitting a corporate inquiry.
38
38
  */
39
39
  export const submitCorporateInquirySchema = z.object({
40
- companyName: z.string().min(2),
41
- contactPerson: z.string().min(2),
42
- designation: z.string().optional(),
43
- email: z.string().email(),
44
- phone: z.string().optional(),
45
- units: z.number().int().positive().optional(),
46
- budgetPerUnit: z.number().optional(),
47
- totalBudget: z.number().optional(),
40
+ companyName: z.string().trim().min(2, "A company name is required.").max(160),
41
+ contactPerson: z.string().trim().min(2, "Who should we reply to?").max(120),
42
+ designation: z.string().trim().max(120).optional(),
43
+ email: z
44
+ .string()
45
+ .trim()
46
+ .toLowerCase()
47
+ .min(1, "An email address is required.")
48
+ .email("That doesn't look like an email address."),
49
+ // Indian mobile, tolerant of spacing and a country code. Optional — a
50
+ // reachable email is enough, and demanding a phone number loses enquiries.
51
+ phone: z
52
+ .string()
53
+ .trim()
54
+ .regex(/^(\+?91[-\s]?)?[6-9]\d{9}$/, "Enter a 10-digit Indian mobile number.")
55
+ .optional()
56
+ .or(z.literal("")),
57
+ /*
58
+ * 🛑 COERCED. These were `z.number()`, and this schema's own form sends
59
+ * strings — so every submission carrying a unit count failed to parse the
60
+ * moment anyone actually wired the form to the schema. Bounds are
61
+ * deliberately generous: they catch a mistyped figure, not a large order.
62
+ *
63
+ * A sales reply is priced from these two, and until now both were free text.
64
+ */
65
+ units: z.coerce
66
+ .number({ invalid_type_error: "Enter the number of units as a number." })
67
+ .int("Units must be a whole number.")
68
+ .positive("At least one unit.")
69
+ .max(1000000, "That looks like a typo — contact us directly for orders this large.")
70
+ .optional(),
71
+ budgetPerUnit: z.coerce
72
+ .number({ invalid_type_error: "Enter a per-unit budget as a number." })
73
+ .min(0, "A budget cannot be negative.")
74
+ .max(10000000, "That looks like a typo.")
75
+ .optional(),
76
+ totalBudget: z.coerce.number().min(0).optional(),
48
77
  deliveryDateRequired: z.string().optional(),
49
78
  customBranding: z.boolean().default(false),
79
+ message: z.string().trim().max(4000).optional(),
50
80
  });
@@ -6,6 +6,8 @@ import { Form } from "../../../ui/components/Form";
6
6
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
7
7
  import { Div, Heading, Stack, Text } from "../../../ui";
8
8
  import { Badge } from "../../../ui/components/Badge";
9
+ import { raffleEntrySchema } from "../../contact/schemas/inquiry-forms";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
9
11
  const __P = {
10
12
  p6: "p-[var(--appkit-space-6)]",
11
13
  };
@@ -38,5 +40,5 @@ export function EventRaffleEntryForm({ prizeLabel, raffleType = "open_raffle", t
38
40
  : raffleType === "top_n_participants"
39
41
  ? `Earliest ${topN ?? "N"} entries qualify.`
40
42
  : "Spin the wheel to claim your prize.";
41
- return (_jsxs(Stack, { className: `${__P.p6}`, gap: "md", rounded: "2xl", border: "default", children: [prizeLabel ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Badge, { variant: "warning", children: "Prize" }), _jsx(Heading, { level: 2, weight: "bold", size: "2xl", children: prizeLabel })] })) : null, _jsx(Text, { size: "sm", color: "muted", children: eligibilityCopy }), submitted ? (_jsx(Div, { textSize: "sm", color: "success", surface: "success-surface", padding: "inline", rounded: "lg", children: "You're in. Check the leaderboard for results." })) : overLimit ? (_jsxs(Div, { textSize: "sm", color: "warning", surface: "warning-surface", padding: "inline", rounded: "lg", children: ["You've used all ", maxEntriesPerUser, " entry slot", maxEntriesPerUser === 1 ? "" : "s", " for this raffle."] })) : (_jsxs(Form, { onSubmit: handleSubmit, className: "space-y-[0.75rem]", children: [_jsx(FieldTextarea, { name: "message", label: "Message", hint: "optional \u2014 share why you'd like to win", value: message, onChange: setMessage, rows: 3, maxLength: 500, placeholder: "Share why you'd like to win", disabled: busy }), _jsx(Button, { type: "submit", variant: "primary", disabled: busy, children: busy ? "Submitting…" : "Enter raffle" })] }))] }));
43
+ return (_jsxs(Stack, { className: `${__P.p6}`, gap: "md", rounded: "2xl", border: "default", children: [prizeLabel ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Badge, { variant: "warning", children: "Prize" }), _jsx(Heading, { level: 2, weight: "bold", size: "2xl", children: prizeLabel })] })) : null, _jsx(Text, { size: "sm", color: "muted", children: eligibilityCopy }), submitted ? (_jsx(Div, { textSize: "sm", color: "success", surface: "success-surface", padding: "inline", rounded: "lg", children: "You're in. Check the leaderboard for results." })) : overLimit ? (_jsxs(Div, { textSize: "sm", color: "warning", surface: "warning-surface", padding: "inline", rounded: "lg", children: ["You've used all ", maxEntriesPerUser, " entry slot", maxEntriesPerUser === 1 ? "" : "s", " for this raffle."] })) : (_jsxs(Form, { schema: raffleEntrySchema, onSubmit: handleSubmit, className: "space-y-[0.75rem]", children: [_jsx(FormErrorSummary, {}), _jsx(FieldTextarea, { name: "message", label: "Message", hint: "optional \u2014 share why you'd like to win", value: message, onChange: setMessage, rows: 3, maxLength: 500, placeholder: "Share why you'd like to win", disabled: busy }), _jsx(Button, { type: "submit", variant: "primary", disabled: busy, children: busy ? "Submitting…" : "Enter raffle" })] }))] }));
42
44
  }
@@ -6,6 +6,8 @@ import { Button, Div, Heading, Span, Text } from "../../../ui";
6
6
  import { DynamicBgDiv } from "../../../ui/components/DynamicBgDiv";
7
7
  import { Form } from "../../../ui/components/Form";
8
8
  import { FieldInput } from "../../../ui/forms/FieldInput";
9
+ import { newsletterSubscribeSchema } from "../../admin/schemas/small-forms";
10
+ import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary";
9
11
  const CLS_ERROR_TEXT = "mt-3 text-[length:var(--appkit-text-sm)] text-error";
10
12
  export function NewsletterBanner({ onSubscribe, eyebrow = "STAY CONNECTED", heading = "Join Our Newsletter", subheading = "Get exclusive offers, new arrivals and more.", placeholder = "Enter your email", ctaLabel = "Subscribe", successMessage = "You're in! Check your inbox.", errorMessage = "Something went wrong. Please try again.", }) {
11
13
  const [email, setEmail] = useState("");
@@ -31,5 +33,5 @@ export function NewsletterBanner({ onSubscribe, eyebrow = "STAY CONNECTED", head
31
33
  setLoading(false);
32
34
  }
33
35
  }
34
- return (_jsxs(DynamicBgDiv, { background: "var(--newsletter-bg, linear-gradient(135deg, var(--appkit-color-primary) 0%, var(--appkit-color-secondary) 100%))", className: "py-[var(--appkit-space-24)] relative overflow-hidden", children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 border border-white/[0.06]", rounded: "full" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 border border-dashed border-white/[0.08]", rounded: "full" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl text-left sm:px-[1.5rem]", padding: "x-md", children: [_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "mb-4 inline-flex border border-white/20 tracking-widest /70", rounded: "full", padding: "pill-md", transform: "uppercase", children: eyebrow }), _jsx(Heading, { color: "inverse", level: 2, smSize: "4xl", size: "3xl", weight: "bold", children: heading }), _jsx(Text, { color: "inverse", className: "mt-4 /60", children: subheading }), success ? (_jsx(Text, { paddingY: "md", color: "inverse", className: "mt-8 border border-white/20 bg-[rgba(255,255,255,0.1)]", paddingX: "x-lg", size: "sm", weight: "semibold", rounded: "xl", children: successMessage })) : (_jsxs(Form, { onSubmit: handleSubmit, className: "mt-8 flex gap-[0.75rem]", children: [_jsx(FieldInput, { name: "email", type: "email", value: email, onChange: setEmail, placeholder: placeholder, required: true, className: "flex-1" }), _jsx(Button, { rounded: "xl", type: "submit", disabled: loading, variant: "primary", paddingX: "lg", textSize: "sm", className: "shrink-0 py-[0.75rem] font-[700] transition-opacity disabled:opacity-60 bg-[var(--color-yellow)] text-black", children: loading ? "…" : ctaLabel })] })), error && (_jsx(Text, { className: CLS_ERROR_TEXT, children: errorMessage })), _jsx(Text, { color: "inverse", className: "mt-4 /40", size: "xs", children: "No spam, ever. Unsubscribe anytime." })] })] }));
36
+ return (_jsxs(DynamicBgDiv, { background: "var(--newsletter-bg, linear-gradient(135deg, var(--appkit-color-primary) 0%, var(--appkit-color-secondary) 100%))", className: "py-[var(--appkit-space-24)] relative overflow-hidden", children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 border border-white/[0.06]", rounded: "full" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 border border-dashed border-white/[0.08]", rounded: "full" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl text-left sm:px-[1.5rem]", padding: "x-md", children: [_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "mb-4 inline-flex border border-white/20 tracking-widest /70", rounded: "full", padding: "pill-md", transform: "uppercase", children: eyebrow }), _jsx(Heading, { color: "inverse", level: 2, smSize: "4xl", size: "3xl", weight: "bold", children: heading }), _jsx(Text, { color: "inverse", className: "mt-4 /60", children: subheading }), success ? (_jsx(Text, { paddingY: "md", color: "inverse", className: "mt-8 border border-white/20 bg-[rgba(255,255,255,0.1)]", paddingX: "x-lg", size: "sm", weight: "semibold", rounded: "xl", children: successMessage })) : (_jsxs(Form, { schema: newsletterSubscribeSchema, onSubmit: handleSubmit, className: "mt-8 flex gap-[0.75rem]", children: [_jsx(FormErrorSummary, {}), _jsx(FieldInput, { name: "email", type: "email", value: email, onChange: setEmail, placeholder: placeholder, required: true, className: "flex-1" }), _jsx(Button, { rounded: "xl", type: "submit", disabled: loading, variant: "primary", paddingX: "lg", textSize: "sm", className: "shrink-0 py-[0.75rem] font-[700] transition-opacity disabled:opacity-60 bg-[var(--color-yellow)] text-black", children: loading ? "…" : ctaLabel })] })), error && (_jsx(Text, { className: CLS_ERROR_TEXT, children: errorMessage })), _jsx(Text, { color: "inverse", className: "mt-4 /40", size: "xs", children: "No spam, ever. Unsubscribe anytime." })] })] }));
35
37
  }
@@ -75,7 +75,7 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
75
75
  if (state === "pending") {
76
76
  return (_jsxs(Div, { className: `${CLS_PENDING_BOX} ${className}`, children: [_jsx(Span, { size: "lg", children: "\u23F3" }), _jsx(Text, { className: CLS_PENDING_TITLE, children: "Offer Pending" }), _jsx(Text, { className: CLS_PENDING_BODY, children: "You already have an offer on this item. Check My Offers for updates." })] }));
77
77
  }
78
- return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
78
+ return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to make an offer. Please log in or create an account to continue." }), _jsx(Button, { variant: "ghost", size: "md", border: "strong", className: `w-full ${className}`, onClick: () => setState("open"), children: "Make Offer" }), _jsx(Modal, { isOpen: state === "open", onClose: () => setState("idle"), size: "md", title: "Make an offer", children: _jsx(Form, { schema: schema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => (_jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Listed at ", fmt(listedPrice), " \u00B7 Minimum offer: ", fmt(minOffer)] }), _jsx(FieldInput, { name: "offerAmount", type: "number", label: "Your offer amount", required: true, value: offerAmount, onChange: (value) => {
79
79
  setOfferAmount(value);
80
80
  clearErrors();
81
81
  }, min: minOffer, max: listedPrice - 1, step: 1, "aria-label": "Offer amount" }), _jsxs(Text, { size: "xs", color: "faint", children: ["Must be between ", fmt(minOffer), " and ", fmt(listedPrice - 1)] }), _jsx(FieldInput, { name: "buyerNote", type: "text", label: "Note to seller (optional)", value: buyerNote, onChange: setBuyerNote, placeholder: "E.g. Bundle deal, long-time fan\u2026", maxLength: 300, "aria-label": "Note to seller" }), _jsx(Text, { size: "xs", color: "muted", children: "The seller will accept, decline, or suggest a counter price." }), _jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Button, { action: ACTIONS.PRODUCT["make-offer"], variant: "primary", size: "sm", type: "button", isLoading: isPending, disabled: isPending, onClick: () => submit(setFieldError, clearErrors), children: isPending ? "Sending…" : `Send offer of ${fmt(Number(offerAmount) || 0)}` }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setState("idle"), disabled: isPending, children: "Cancel" })] })] })) }) })] }));
package/dist/index.d.ts CHANGED
@@ -3284,3 +3284,5 @@ export { withRetry } from "./http/retry";
3284
3284
  export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFirebaseAuthNormalized, isFirebaseFirestoreNormalized, isFirebaseStorageNormalized, isNativeNormalized, isNetworkNormalized, isUnknownNormalized, isZodNormalized, } from "./errors/normalize";
3285
3285
  export type { NormalizedError, NormalizedApiError, NormalizedAppError, NormalizedFirebaseAuthError, NormalizedFirebaseFirestoreError, NormalizedFirebaseStorageError, NormalizedNativeError, NormalizedNetworkError, NormalizedUnknownThrownValue, NormalizedZodError, } from "./errors/normalize";
3286
3286
  export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, type CartLane, type LaneAssignable, } from "./_internal/shared/checkout/lanes";
3287
+ export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
3288
+ export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
package/dist/index.js CHANGED
@@ -5693,3 +5693,5 @@ export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFi
5693
5693
  // Checkout lanes — the derived auction > offer > standard partition of the
5694
5694
  // cart, and the priority rule that decides which one may be checked out.
5695
5695
  export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, } from "./_internal/shared/checkout/lanes";
5696
+ export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
5697
+ export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.16.1",
3
+ "version": "4.17.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"