@formhaus/core 0.1.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/engine/form-engine.d.ts +45 -0
- package/dist/engine/form-engine.d.ts.map +1 -0
- package/dist/engine/form-engine.js +278 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/schema-validation/index.d.ts +2 -0
- package/dist/schema-validation/index.d.ts.map +1 -0
- package/dist/schema-validation/index.js +1 -0
- package/dist/schema-validation/validate-schema.d.ts +3 -0
- package/dist/schema-validation/validate-schema.d.ts.map +1 -0
- package/dist/schema-validation/validate-schema.js +69 -0
- package/dist/types/action.d.ts +8 -0
- package/dist/types/action.d.ts.map +1 -0
- package/dist/types/action.js +1 -0
- package/dist/types/analytics.d.ts +27 -0
- package/dist/types/analytics.d.ts.map +1 -0
- package/dist/types/analytics.js +1 -0
- package/dist/types/field.d.ts +48 -0
- package/dist/types/field.d.ts.map +1 -0
- package/dist/types/field.js +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/schema.d.ts +21 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +1 -0
- package/dist/validation/default-messages.d.ts +2 -0
- package/dist/validation/default-messages.d.ts.map +1 -0
- package/dist/validation/default-messages.js +20 -0
- package/dist/validation/index.d.ts +4 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +3 -0
- package/dist/validation/validate-field.d.ts +4 -0
- package/dist/validation/validate-field.d.ts.map +1 -0
- package/dist/validation/validate-field.js +61 -0
- package/dist/validation/validate-step.d.ts +5 -0
- package/dist/validation/validate-step.d.ts.map +1 -0
- package/dist/validation/validate-step.js +26 -0
- package/dist/visibility/evaluate-condition.d.ts +3 -0
- package/dist/visibility/evaluate-condition.d.ts.map +1 -0
- package/dist/visibility/evaluate-condition.js +19 -0
- package/dist/visibility/index.d.ts +4 -0
- package/dist/visibility/index.d.ts.map +1 -0
- package/dist/visibility/index.js +3 -0
- package/dist/visibility/is-step-visible.d.ts +3 -0
- package/dist/visibility/is-step-visible.d.ts.map +1 -0
- package/dist/visibility/is-step-visible.js +4 -0
- package/dist/visibility/is-visible.d.ts +3 -0
- package/dist/visibility/is-visible.d.ts.map +1 -0
- package/dist/visibility/is-visible.js +14 -0
- package/fixtures/basic-form.json +46 -0
- package/fixtures/conditional-fields.json +84 -0
- package/fixtures/conditional-steps.json +79 -0
- package/fixtures/dispute-form.json +91 -0
- package/fixtures/multi-step-form.json +108 -0
- package/fixtures/validation-form.json +62 -0
- package/package.json +33 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getDefaultMessage } from './default-messages';
|
|
2
|
+
function isEmpty(value) {
|
|
3
|
+
return value === undefined || value === null || value === '';
|
|
4
|
+
}
|
|
5
|
+
export function validateField(field, value, allValues, validators) {
|
|
6
|
+
const rules = field.validation;
|
|
7
|
+
if (!rules)
|
|
8
|
+
return null;
|
|
9
|
+
if (rules.required) {
|
|
10
|
+
if (isEmpty(value)) {
|
|
11
|
+
return typeof rules.required === 'string' ? rules.required : getDefaultMessage('required');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Skip remaining rules if value is empty and not required
|
|
15
|
+
if (isEmpty(value))
|
|
16
|
+
return null;
|
|
17
|
+
if (rules.minLength !== undefined && typeof value === 'string') {
|
|
18
|
+
if (value.length < rules.minLength) {
|
|
19
|
+
return rules.minLengthMessage ?? getDefaultMessage('minLength', { min: rules.minLength });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (rules.maxLength !== undefined && typeof value === 'string') {
|
|
23
|
+
if (value.length > rules.maxLength) {
|
|
24
|
+
return rules.maxLengthMessage ?? getDefaultMessage('maxLength', { max: rules.maxLength });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (rules.min !== undefined && typeof value === 'number') {
|
|
28
|
+
if (value < rules.min) {
|
|
29
|
+
return rules.minMessage ?? getDefaultMessage('min', { min: rules.min });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (rules.max !== undefined && typeof value === 'number') {
|
|
33
|
+
if (value > rules.max) {
|
|
34
|
+
return rules.maxMessage ?? getDefaultMessage('max', { max: rules.max });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (rules.pattern !== undefined) {
|
|
38
|
+
try {
|
|
39
|
+
const regex = new RegExp(rules.pattern);
|
|
40
|
+
if (!regex.test(String(value))) {
|
|
41
|
+
return rules.patternMessage ?? getDefaultMessage('pattern');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Invalid regex: skip validation silently
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (rules.matchField !== undefined) {
|
|
49
|
+
const otherValue = allValues[rules.matchField];
|
|
50
|
+
if (value !== otherValue) {
|
|
51
|
+
return rules.matchFieldMessage ?? getDefaultMessage('matchField');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (rules.validator !== undefined && validators) {
|
|
55
|
+
const customValidator = validators[rules.validator];
|
|
56
|
+
if (customValidator) {
|
|
57
|
+
return customValidator(value, allValues);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FormField, FormStep } from '../types';
|
|
2
|
+
import { type ValidatorFn } from './validate-field';
|
|
3
|
+
export declare function validateStep(step: FormStep, values: Record<string, unknown>, validators?: Record<string, ValidatorFn>): Record<string, string>;
|
|
4
|
+
export declare function validateFields(fields: FormField[], values: Record<string, unknown>, validators?: Record<string, ValidatorFn>): Record<string, string>;
|
|
5
|
+
//# sourceMappingURL=validate-step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-step.d.ts","sourceRoot":"","sources":["../../src/validation/validate-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,kBAAkB,CAAC;AAEnE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACvC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxB;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACvC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isVisible } from '../visibility';
|
|
2
|
+
import { validateField } from './validate-field';
|
|
3
|
+
export function validateStep(step, values, validators) {
|
|
4
|
+
const errors = {};
|
|
5
|
+
for (const field of step.fields) {
|
|
6
|
+
if (!isVisible(field, values))
|
|
7
|
+
continue;
|
|
8
|
+
const error = validateField(field, values[field.key], values, validators);
|
|
9
|
+
if (error) {
|
|
10
|
+
errors[field.key] = error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return errors;
|
|
14
|
+
}
|
|
15
|
+
export function validateFields(fields, values, validators) {
|
|
16
|
+
const errors = {};
|
|
17
|
+
for (const field of fields) {
|
|
18
|
+
if (!isVisible(field, values))
|
|
19
|
+
continue;
|
|
20
|
+
const error = validateField(field, values[field.key], values, validators);
|
|
21
|
+
if (error) {
|
|
22
|
+
errors[field.key] = error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return errors;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate-condition.d.ts","sourceRoot":"","sources":["../../src/visibility/evaluate-condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAwBT"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function evaluateCondition(condition, values) {
|
|
2
|
+
const value = values[condition.field];
|
|
3
|
+
if (condition.eq !== undefined) {
|
|
4
|
+
return value === condition.eq;
|
|
5
|
+
}
|
|
6
|
+
if (condition.neq !== undefined) {
|
|
7
|
+
return value !== condition.neq;
|
|
8
|
+
}
|
|
9
|
+
if (condition.in !== undefined) {
|
|
10
|
+
return condition.in.includes(value);
|
|
11
|
+
}
|
|
12
|
+
if (condition.notIn !== undefined) {
|
|
13
|
+
return !condition.notIn.includes(value);
|
|
14
|
+
}
|
|
15
|
+
if (condition.notEmpty === true) {
|
|
16
|
+
return value !== undefined && value !== null && value !== '';
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/visibility/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-step-visible.d.ts","sourceRoot":"","sources":["../../src/visibility/is-step-visible.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGzC,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAEtF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-visible.d.ts","sourceRoot":"","sources":["../../src/visibility/is-visible.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,UAAU,CAAC;AAgBzD,wBAAgB,SAAS,CACvB,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,EAC1C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAIT"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { evaluateCondition } from './evaluate-condition';
|
|
2
|
+
function evaluateConditions(conditions, mode, values) {
|
|
3
|
+
if (!conditions || conditions.length === 0)
|
|
4
|
+
return true;
|
|
5
|
+
if (mode === 'and') {
|
|
6
|
+
return conditions.every((c) => evaluateCondition(c, values));
|
|
7
|
+
}
|
|
8
|
+
return conditions.some((c) => evaluateCondition(c, values));
|
|
9
|
+
}
|
|
10
|
+
export function isVisible(field, values) {
|
|
11
|
+
const showResult = evaluateConditions(field.show, 'and', values);
|
|
12
|
+
const showAnyResult = evaluateConditions(field.showAny, 'or', values);
|
|
13
|
+
return showResult && showAnyResult;
|
|
14
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "basic-form",
|
|
3
|
+
"title": "Contact Information",
|
|
4
|
+
"submit": { "label": "Submit" },
|
|
5
|
+
"fields": [
|
|
6
|
+
{
|
|
7
|
+
"key": "firstName",
|
|
8
|
+
"type": "text",
|
|
9
|
+
"label": "First Name",
|
|
10
|
+
"placeholder": "Enter your first name",
|
|
11
|
+
"validation": { "required": true, "minLength": 2 }
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"key": "lastName",
|
|
15
|
+
"type": "text",
|
|
16
|
+
"label": "Last Name",
|
|
17
|
+
"placeholder": "Enter your last name",
|
|
18
|
+
"validation": { "required": true }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"key": "email",
|
|
22
|
+
"type": "email",
|
|
23
|
+
"label": "Email Address",
|
|
24
|
+
"placeholder": "you@example.com",
|
|
25
|
+
"validation": {
|
|
26
|
+
"required": true,
|
|
27
|
+
"pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
28
|
+
"patternMessage": "Please enter a valid email address"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"key": "country",
|
|
33
|
+
"type": "select",
|
|
34
|
+
"label": "Country",
|
|
35
|
+
"placeholder": "Select a country",
|
|
36
|
+
"options": [
|
|
37
|
+
{ "value": "US", "label": "United States" },
|
|
38
|
+
{ "value": "MX", "label": "Mexico" },
|
|
39
|
+
{ "value": "CA", "label": "Canada" },
|
|
40
|
+
{ "value": "BR", "label": "Brazil" },
|
|
41
|
+
{ "value": "IN", "label": "India" }
|
|
42
|
+
],
|
|
43
|
+
"validation": { "required": true }
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conditional-fields",
|
|
3
|
+
"title": "Payment Details",
|
|
4
|
+
"submit": { "label": "Continue" },
|
|
5
|
+
"fields": [
|
|
6
|
+
{
|
|
7
|
+
"key": "paymentMethod",
|
|
8
|
+
"type": "select",
|
|
9
|
+
"label": "Payment Method",
|
|
10
|
+
"options": [
|
|
11
|
+
{ "value": "bank", "label": "Bank Transfer" },
|
|
12
|
+
{ "value": "card", "label": "Credit/Debit Card" },
|
|
13
|
+
{ "value": "crypto", "label": "Cryptocurrency" }
|
|
14
|
+
],
|
|
15
|
+
"validation": { "required": true }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"key": "bankName",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"label": "Bank Name",
|
|
21
|
+
"show": [{ "field": "paymentMethod", "eq": "bank" }],
|
|
22
|
+
"validation": { "required": true }
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "accountNumber",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"label": "Account Number",
|
|
28
|
+
"show": [{ "field": "paymentMethod", "eq": "bank" }],
|
|
29
|
+
"validation": { "required": true },
|
|
30
|
+
"inputMode": "numeric"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"key": "routingNumber",
|
|
34
|
+
"type": "text",
|
|
35
|
+
"label": "Routing Number",
|
|
36
|
+
"show": [{ "field": "paymentMethod", "eq": "bank" }],
|
|
37
|
+
"validation": {
|
|
38
|
+
"required": true,
|
|
39
|
+
"pattern": "^[0-9]{9}$",
|
|
40
|
+
"patternMessage": "Routing number must be 9 digits"
|
|
41
|
+
},
|
|
42
|
+
"inputMode": "numeric"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "cardNumber",
|
|
46
|
+
"type": "text",
|
|
47
|
+
"label": "Card Number",
|
|
48
|
+
"show": [{ "field": "paymentMethod", "eq": "card" }],
|
|
49
|
+
"validation": { "required": true },
|
|
50
|
+
"inputMode": "numeric"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"key": "cardExpiry",
|
|
54
|
+
"type": "text",
|
|
55
|
+
"label": "Expiry (MM/YY)",
|
|
56
|
+
"placeholder": "MM/YY",
|
|
57
|
+
"show": [{ "field": "paymentMethod", "eq": "card" }],
|
|
58
|
+
"validation": {
|
|
59
|
+
"required": true,
|
|
60
|
+
"pattern": "^(0[1-9]|1[0-2])/[0-9]{2}$",
|
|
61
|
+
"patternMessage": "Format: MM/YY"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"key": "walletAddress",
|
|
66
|
+
"type": "text",
|
|
67
|
+
"label": "Wallet Address",
|
|
68
|
+
"show": [{ "field": "paymentMethod", "eq": "crypto" }],
|
|
69
|
+
"validation": { "required": true }
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"key": "cryptoNetwork",
|
|
73
|
+
"type": "select",
|
|
74
|
+
"label": "Network",
|
|
75
|
+
"show": [{ "field": "paymentMethod", "eq": "crypto" }],
|
|
76
|
+
"options": [
|
|
77
|
+
{ "value": "ethereum", "label": "Ethereum" },
|
|
78
|
+
{ "value": "tron", "label": "Tron" },
|
|
79
|
+
{ "value": "solana", "label": "Solana" }
|
|
80
|
+
],
|
|
81
|
+
"validation": { "required": true }
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "conditional-steps",
|
|
3
|
+
"title": "Open Account",
|
|
4
|
+
"submit": { "label": "Open Account" },
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"id": "account-type",
|
|
8
|
+
"title": "Account Type",
|
|
9
|
+
"fields": [
|
|
10
|
+
{
|
|
11
|
+
"key": "accountType",
|
|
12
|
+
"type": "radio",
|
|
13
|
+
"label": "What type of account?",
|
|
14
|
+
"options": [
|
|
15
|
+
{ "value": "personal", "label": "Personal" },
|
|
16
|
+
{ "value": "business", "label": "Business" }
|
|
17
|
+
],
|
|
18
|
+
"validation": { "required": true }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"key": "fullName",
|
|
22
|
+
"type": "text",
|
|
23
|
+
"label": "Full Name",
|
|
24
|
+
"validation": { "required": true }
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"key": "email",
|
|
28
|
+
"type": "email",
|
|
29
|
+
"label": "Email",
|
|
30
|
+
"validation": { "required": true }
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "business-info",
|
|
36
|
+
"title": "Business Information",
|
|
37
|
+
"description": "Tell us about your business",
|
|
38
|
+
"show": [{ "field": "accountType", "eq": "business" }],
|
|
39
|
+
"fields": [
|
|
40
|
+
{
|
|
41
|
+
"key": "companyName",
|
|
42
|
+
"type": "text",
|
|
43
|
+
"label": "Company Name",
|
|
44
|
+
"validation": { "required": true }
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"key": "taxId",
|
|
48
|
+
"type": "text",
|
|
49
|
+
"label": "Tax ID / EIN",
|
|
50
|
+
"validation": { "required": true }
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"key": "companySize",
|
|
54
|
+
"type": "select",
|
|
55
|
+
"label": "Company Size",
|
|
56
|
+
"options": [
|
|
57
|
+
{ "value": "1-10", "label": "1-10 employees" },
|
|
58
|
+
{ "value": "11-50", "label": "11-50 employees" },
|
|
59
|
+
{ "value": "51-200", "label": "51-200 employees" },
|
|
60
|
+
{ "value": "200+", "label": "200+ employees" }
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "review",
|
|
67
|
+
"title": "Review & Confirm",
|
|
68
|
+
"description": "Please review your information",
|
|
69
|
+
"fields": [
|
|
70
|
+
{
|
|
71
|
+
"key": "termsAccepted",
|
|
72
|
+
"type": "checkbox",
|
|
73
|
+
"label": "I accept the Terms of Service and Privacy Policy",
|
|
74
|
+
"validation": { "required": "You must accept the terms to continue" }
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dispute-form",
|
|
3
|
+
"title": "File a Dispute",
|
|
4
|
+
"submit": { "label": "Submit Dispute" },
|
|
5
|
+
"cancel": { "label": "Cancel", "variant": "text" },
|
|
6
|
+
"steps": [
|
|
7
|
+
{
|
|
8
|
+
"id": "transaction",
|
|
9
|
+
"title": "Transaction Details",
|
|
10
|
+
"description": "Which transaction are you disputing?",
|
|
11
|
+
"fields": [
|
|
12
|
+
{
|
|
13
|
+
"key": "transactionId",
|
|
14
|
+
"type": "text",
|
|
15
|
+
"label": "Transaction ID",
|
|
16
|
+
"helperText": "Found in your transaction history",
|
|
17
|
+
"validation": { "required": true }
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"key": "disputeType",
|
|
21
|
+
"type": "radio",
|
|
22
|
+
"label": "Reason for Dispute",
|
|
23
|
+
"options": [
|
|
24
|
+
{ "value": "unauthorized", "label": "Unauthorized transaction" },
|
|
25
|
+
{ "value": "not_received", "label": "Transfer not received" },
|
|
26
|
+
{ "value": "wrong_amount", "label": "Wrong amount" },
|
|
27
|
+
{ "value": "duplicate", "label": "Duplicate charge" },
|
|
28
|
+
{ "value": "other", "label": "Other" }
|
|
29
|
+
],
|
|
30
|
+
"validation": { "required": true }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"key": "disputeAmount",
|
|
34
|
+
"type": "number",
|
|
35
|
+
"label": "Disputed Amount (USD)",
|
|
36
|
+
"show": [{ "field": "disputeType", "eq": "wrong_amount" }],
|
|
37
|
+
"validation": { "required": true, "min": 0.01 },
|
|
38
|
+
"inputMode": "numeric"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "details",
|
|
44
|
+
"title": "Additional Details",
|
|
45
|
+
"fields": [
|
|
46
|
+
{
|
|
47
|
+
"key": "description",
|
|
48
|
+
"type": "textarea",
|
|
49
|
+
"label": "Describe the issue",
|
|
50
|
+
"helperText": "Please provide as much detail as possible",
|
|
51
|
+
"validation": {
|
|
52
|
+
"required": true,
|
|
53
|
+
"minLength": 20,
|
|
54
|
+
"minLengthMessage": "Please provide more detail (at least 20 characters)"
|
|
55
|
+
},
|
|
56
|
+
"rows": 5
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"key": "contactPreference",
|
|
60
|
+
"type": "select",
|
|
61
|
+
"label": "How should we contact you?",
|
|
62
|
+
"options": [
|
|
63
|
+
{ "value": "email", "label": "Email" },
|
|
64
|
+
{ "value": "phone", "label": "Phone" }
|
|
65
|
+
],
|
|
66
|
+
"defaultValue": "email"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"key": "phoneNumber",
|
|
70
|
+
"type": "phone",
|
|
71
|
+
"label": "Phone Number",
|
|
72
|
+
"show": [{ "field": "contactPreference", "eq": "phone" }],
|
|
73
|
+
"validation": { "required": true },
|
|
74
|
+
"inputMode": "tel"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "confirm",
|
|
80
|
+
"title": "Review & Submit",
|
|
81
|
+
"fields": [
|
|
82
|
+
{
|
|
83
|
+
"key": "acknowledgement",
|
|
84
|
+
"type": "checkbox",
|
|
85
|
+
"label": "I certify that the information provided is accurate and complete",
|
|
86
|
+
"validation": { "required": "You must certify the information is accurate" }
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "multi-step",
|
|
3
|
+
"title": "Account Setup",
|
|
4
|
+
"submit": { "label": "Create Account" },
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"id": "personal",
|
|
8
|
+
"title": "Personal Information",
|
|
9
|
+
"description": "Tell us about yourself",
|
|
10
|
+
"fields": [
|
|
11
|
+
{
|
|
12
|
+
"key": "firstName",
|
|
13
|
+
"type": "text",
|
|
14
|
+
"label": "First Name",
|
|
15
|
+
"validation": { "required": true }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"key": "lastName",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"label": "Last Name",
|
|
21
|
+
"validation": { "required": true }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"key": "email",
|
|
25
|
+
"type": "email",
|
|
26
|
+
"label": "Email",
|
|
27
|
+
"validation": { "required": true }
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"key": "phone",
|
|
31
|
+
"type": "phone",
|
|
32
|
+
"label": "Phone Number",
|
|
33
|
+
"inputMode": "tel"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "address",
|
|
39
|
+
"title": "Address",
|
|
40
|
+
"description": "Where do you live?",
|
|
41
|
+
"fields": [
|
|
42
|
+
{
|
|
43
|
+
"key": "street",
|
|
44
|
+
"type": "text",
|
|
45
|
+
"label": "Street Address",
|
|
46
|
+
"validation": { "required": true }
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"key": "city",
|
|
50
|
+
"type": "text",
|
|
51
|
+
"label": "City",
|
|
52
|
+
"validation": { "required": true }
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"key": "state",
|
|
56
|
+
"type": "text",
|
|
57
|
+
"label": "State / Province"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"key": "zipCode",
|
|
61
|
+
"type": "text",
|
|
62
|
+
"label": "ZIP / Postal Code",
|
|
63
|
+
"validation": { "required": true }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"key": "country",
|
|
67
|
+
"type": "select",
|
|
68
|
+
"label": "Country",
|
|
69
|
+
"options": [
|
|
70
|
+
{ "value": "US", "label": "United States" },
|
|
71
|
+
{ "value": "MX", "label": "Mexico" },
|
|
72
|
+
{ "value": "CA", "label": "Canada" }
|
|
73
|
+
],
|
|
74
|
+
"validation": { "required": true }
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "preferences",
|
|
80
|
+
"title": "Preferences",
|
|
81
|
+
"description": "Customize your experience",
|
|
82
|
+
"fields": [
|
|
83
|
+
{
|
|
84
|
+
"key": "newsletter",
|
|
85
|
+
"type": "checkbox",
|
|
86
|
+
"label": "Subscribe to newsletter",
|
|
87
|
+
"defaultValue": true
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"key": "notifications",
|
|
91
|
+
"type": "radio",
|
|
92
|
+
"label": "Notification preference",
|
|
93
|
+
"options": [
|
|
94
|
+
{ "value": "email", "label": "Email" },
|
|
95
|
+
{ "value": "sms", "label": "SMS" },
|
|
96
|
+
{ "value": "none", "label": "None" }
|
|
97
|
+
],
|
|
98
|
+
"defaultValue": "email"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"key": "darkMode",
|
|
102
|
+
"type": "switch",
|
|
103
|
+
"label": "Dark mode"
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "validation-form",
|
|
3
|
+
"title": "Account Registration",
|
|
4
|
+
"submit": { "label": "Create Account" },
|
|
5
|
+
"fields": [
|
|
6
|
+
{
|
|
7
|
+
"key": "username",
|
|
8
|
+
"type": "text",
|
|
9
|
+
"label": "Username",
|
|
10
|
+
"placeholder": "Choose a username",
|
|
11
|
+
"validation": {
|
|
12
|
+
"required": true,
|
|
13
|
+
"minLength": 3,
|
|
14
|
+
"maxLength": 20,
|
|
15
|
+
"pattern": "^[a-zA-Z0-9_]+$",
|
|
16
|
+
"patternMessage": "Only letters, numbers, and underscores"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"key": "email",
|
|
21
|
+
"type": "email",
|
|
22
|
+
"label": "Email",
|
|
23
|
+
"validation": {
|
|
24
|
+
"required": true,
|
|
25
|
+
"pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
26
|
+
"patternMessage": "Please enter a valid email"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"key": "password",
|
|
31
|
+
"type": "password",
|
|
32
|
+
"label": "Password",
|
|
33
|
+
"helperText": "At least 8 characters",
|
|
34
|
+
"validation": {
|
|
35
|
+
"required": true,
|
|
36
|
+
"minLength": 8,
|
|
37
|
+
"minLengthMessage": "Password must be at least 8 characters"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"key": "confirmPassword",
|
|
42
|
+
"type": "password",
|
|
43
|
+
"label": "Confirm Password",
|
|
44
|
+
"validation": {
|
|
45
|
+
"required": true,
|
|
46
|
+
"matchField": "password",
|
|
47
|
+
"matchFieldMessage": "Passwords do not match"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"key": "age",
|
|
52
|
+
"type": "number",
|
|
53
|
+
"label": "Age",
|
|
54
|
+
"validation": {
|
|
55
|
+
"required": true,
|
|
56
|
+
"min": 18,
|
|
57
|
+
"minMessage": "You must be at least 18 years old",
|
|
58
|
+
"max": 120
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|