@stapel/forms-react 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/CHANGELOG.md +62 -0
- package/MODULE.md +172 -0
- package/README.md +207 -0
- package/dist/api/export.d.ts +66 -0
- package/dist/api/export.d.ts.map +1 -0
- package/dist/api/export.js +138 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/extensions.d.ts +8 -0
- package/dist/api/extensions.d.ts.map +1 -0
- package/dist/api/extensions.js +8 -0
- package/dist/api/extensions.js.map +1 -0
- package/dist/api/formsApi.d.ts +113 -0
- package/dist/api/formsApi.d.ts.map +1 -0
- package/dist/api/formsApi.js +81 -0
- package/dist/api/formsApi.js.map +1 -0
- package/dist/api/generated/schema.d.ts +987 -0
- package/dist/api/generated/schema.d.ts.map +1 -0
- package/dist/api/generated/schema.js +2 -0
- package/dist/api/generated/schema.js.map +1 -0
- package/dist/api/types.d.ts +222 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +14 -0
- package/dist/api/types.js.map +1 -0
- package/dist/default/ConfigField.d.ts +32 -0
- package/dist/default/ConfigField.d.ts.map +1 -0
- package/dist/default/ConfigField.js +101 -0
- package/dist/default/ConfigField.js.map +1 -0
- package/dist/default/ErrorAlert.d.ts +9 -0
- package/dist/default/ErrorAlert.d.ts.map +1 -0
- package/dist/default/ErrorAlert.js +27 -0
- package/dist/default/ErrorAlert.js.map +1 -0
- package/dist/default/FormBuilderPane.d.ts +36 -0
- package/dist/default/FormBuilderPane.d.ts.map +1 -0
- package/dist/default/FormBuilderPane.js +70 -0
- package/dist/default/FormBuilderPane.js.map +1 -0
- package/dist/default/FormsListPane.d.ts +18 -0
- package/dist/default/FormsListPane.d.ts.map +1 -0
- package/dist/default/FormsListPane.js +28 -0
- package/dist/default/FormsListPane.js.map +1 -0
- package/dist/default/ResponsesPane.d.ts +21 -0
- package/dist/default/ResponsesPane.d.ts.map +1 -0
- package/dist/default/ResponsesPane.js +118 -0
- package/dist/default/ResponsesPane.js.map +1 -0
- package/dist/default/StapelForm.d.ts +76 -0
- package/dist/default/StapelForm.d.ts.map +1 -0
- package/dist/default/StapelForm.js +118 -0
- package/dist/default/StapelForm.js.map +1 -0
- package/dist/default/fields.d.ts +11 -0
- package/dist/default/fields.d.ts.map +1 -0
- package/dist/default/fields.js +273 -0
- package/dist/default/fields.js.map +1 -0
- package/dist/default/index.d.ts +42 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +37 -0
- package/dist/default/index.js.map +1 -0
- package/dist/default/slots.d.ts +59 -0
- package/dist/default/slots.d.ts.map +1 -0
- package/dist/default/slots.js +25 -0
- package/dist/default/slots.js.map +1 -0
- package/dist/default/theme.d.ts +23 -0
- package/dist/default/theme.d.ts.map +1 -0
- package/dist/default/theme.js +38 -0
- package/dist/default/theme.js.map +1 -0
- package/dist/default/types.d.ts +12 -0
- package/dist/default/types.d.ts.map +1 -0
- package/dist/default/types.js +2 -0
- package/dist/default/types.js.map +1 -0
- package/dist/flows/errors.d.ts +12 -0
- package/dist/flows/errors.d.ts.map +1 -0
- package/dist/flows/errors.js +13 -0
- package/dist/flows/errors.js.map +1 -0
- package/dist/flows/registry.d.ts +22 -0
- package/dist/flows/registry.d.ts.map +1 -0
- package/dist/flows/registry.js +20 -0
- package/dist/flows/registry.js.map +1 -0
- package/dist/headless/FormBuilder.d.ts +100 -0
- package/dist/headless/FormBuilder.d.ts.map +1 -0
- package/dist/headless/FormBuilder.js +278 -0
- package/dist/headless/FormBuilder.js.map +1 -0
- package/dist/headless/FormFill.d.ts +88 -0
- package/dist/headless/FormFill.d.ts.map +1 -0
- package/dist/headless/FormFill.js +279 -0
- package/dist/headless/FormFill.js.map +1 -0
- package/dist/headless/FormList.d.ts +34 -0
- package/dist/headless/FormList.d.ts.map +1 -0
- package/dist/headless/FormList.js +37 -0
- package/dist/headless/FormList.js.map +1 -0
- package/dist/headless/FormsProvider.d.ts +19 -0
- package/dist/headless/FormsProvider.d.ts.map +1 -0
- package/dist/headless/FormsProvider.js +15 -0
- package/dist/headless/FormsProvider.js.map +1 -0
- package/dist/headless/ResponsesTable.d.ts +82 -0
- package/dist/headless/ResponsesTable.d.ts.map +1 -0
- package/dist/headless/ResponsesTable.js +195 -0
- package/dist/headless/ResponsesTable.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +10 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +20 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +7 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +118 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +79 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +485 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +246 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +79 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +141 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +233 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +7 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +127 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +11 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +16 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/mutations.d.ts +135 -0
- package/dist/model/mutations.d.ts.map +1 -0
- package/dist/model/mutations.js +223 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/queries.d.ts +59 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +117 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +28 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +23 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/runtime.d.ts +28 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +14 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/widgets/configForms.d.ts +112 -0
- package/dist/widgets/configForms.d.ts.map +1 -0
- package/dist/widgets/configForms.js +211 -0
- package/dist/widgets/configForms.js.map +1 -0
- package/dist/widgets/registry.d.ts +83 -0
- package/dist/widgets/registry.d.ts.map +1 -0
- package/dist/widgets/registry.js +27 -0
- package/dist/widgets/registry.js.map +1 -0
- package/dist/widgets/validate.d.ts +57 -0
- package/dist/widgets/validate.d.ts.map +1 -0
- package/dist/widgets/validate.js +173 -0
- package/dist/widgets/validate.js.map +1 -0
- package/llms.txt +124 -0
- package/manifest.json +1038 -0
- package/package.json +108 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/export.ts +195 -0
- package/src/api/extensions.ts +7 -0
- package/src/api/formsApi.ts +329 -0
- package/src/api/generated/schema.ts +989 -0
- package/src/api/types.ts +276 -0
- package/src/default/ConfigField.tsx +227 -0
- package/src/default/ErrorAlert.tsx +46 -0
- package/src/default/FormBuilderPane.tsx +414 -0
- package/src/default/FormsListPane.tsx +144 -0
- package/src/default/ResponsesPane.tsx +332 -0
- package/src/default/StapelForm.tsx +377 -0
- package/src/default/fields.tsx +454 -0
- package/src/default/index.ts +71 -0
- package/src/default/slots.ts +78 -0
- package/src/default/theme.tsx +65 -0
- package/src/default/types.ts +12 -0
- package/src/flows/errors.ts +16 -0
- package/src/flows/registry.ts +32 -0
- package/src/headless/FormBuilder.tsx +453 -0
- package/src/headless/FormFill.tsx +396 -0
- package/src/headless/FormList.tsx +74 -0
- package/src/headless/FormsProvider.tsx +20 -0
- package/src/headless/ResponsesTable.tsx +341 -0
- package/src/i18n/errorsMap.ts +31 -0
- package/src/i18n/es.ts +140 -0
- package/src/i18n/generated/errors.es.gen.ts +85 -0
- package/src/i18n/generated/errors.gen.ts +277 -0
- package/src/i18n/generated/errors.json +583 -0
- package/src/i18n/generated/errors.ru.gen.ts +85 -0
- package/src/i18n/keys.ts +264 -0
- package/src/i18n/ru.ts +150 -0
- package/src/index.ts +184 -0
- package/src/model/context.tsx +28 -0
- package/src/model/mutations.ts +387 -0
- package/src/model/queries.ts +154 -0
- package/src/model/queryKeys.ts +61 -0
- package/src/model/runtime.ts +48 -0
- package/src/widgets/registry.ts +101 -0
- package/src/widgets/validate.ts +224 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/** The `error.400.feature_*` keys this mirror can raise — a subset of
|
|
2
|
+
* `stapel_attributes.errors.ATTRIBUTES_ERRORS`, listed so the i18n bundle can
|
|
3
|
+
* be checked against it. */
|
|
4
|
+
export const FEATURE_ERROR_CODES = [
|
|
5
|
+
"error.400.feature_below_minimum",
|
|
6
|
+
"error.400.feature_above_maximum",
|
|
7
|
+
"error.400.feature_not_in_options",
|
|
8
|
+
"error.400.feature_invalid_type",
|
|
9
|
+
"error.400.feature_invalid_format",
|
|
10
|
+
"error.400.feature_mandatory_missing",
|
|
11
|
+
];
|
|
12
|
+
function featureError(field, code, extra = {}) {
|
|
13
|
+
return {
|
|
14
|
+
code,
|
|
15
|
+
// `field` is what the fleet's `useFieldError` routes on; `slug` and
|
|
16
|
+
// `feature` are what the attributes catalogue's message templates
|
|
17
|
+
// interpolate. The adapter keeps all three server-side (delta note 1), so
|
|
18
|
+
// the mirror does too.
|
|
19
|
+
params: {
|
|
20
|
+
field: field.slug,
|
|
21
|
+
slug: field.slug,
|
|
22
|
+
feature: field.name ?? field.slug,
|
|
23
|
+
...extra,
|
|
24
|
+
},
|
|
25
|
+
status: 400,
|
|
26
|
+
message: undefined,
|
|
27
|
+
language: undefined,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function num(config, key) {
|
|
31
|
+
const raw = config[key];
|
|
32
|
+
return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
|
|
33
|
+
}
|
|
34
|
+
/** The declared choice values of an options-bearing config, or `undefined`
|
|
35
|
+
* when the kind has none. Accepts both the bare-scalar and the
|
|
36
|
+
* `{value, label}` option shapes the attributes types allow. */
|
|
37
|
+
export function optionValues(field) {
|
|
38
|
+
const raw = (field.config ?? {})["options"];
|
|
39
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
return raw.map((option) => option !== null && typeof option === "object" && "value" in option
|
|
42
|
+
? option.value
|
|
43
|
+
: option);
|
|
44
|
+
}
|
|
45
|
+
/** True when the answer is "nothing was entered" — the one shape the
|
|
46
|
+
* mandatory rule fires on. An explicit `false` is an ANSWER for a `bool`, not
|
|
47
|
+
* an absence, which is why this is not a falsiness check. */
|
|
48
|
+
export function isBlank(value) {
|
|
49
|
+
if (value === undefined || value === null)
|
|
50
|
+
return true;
|
|
51
|
+
if (typeof value === "string")
|
|
52
|
+
return value.trim().length === 0;
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.length === 0;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate one answer against its field's config. Returns the mirrored
|
|
59
|
+
* refusal, or `undefined` when this side of the wire is satisfied.
|
|
60
|
+
*
|
|
61
|
+
* `header` fields are skipped outright: the engine regenerates a header's DAO
|
|
62
|
+
* from its config and REJECTS an answer to one (backend delta note 1), so a
|
|
63
|
+
* header has nothing to validate and must never be given a value.
|
|
64
|
+
*/
|
|
65
|
+
export function validateFieldValue(field, value) {
|
|
66
|
+
if (field.kind === "header")
|
|
67
|
+
return undefined;
|
|
68
|
+
const config = field.config ?? {};
|
|
69
|
+
if (isBlank(value)) {
|
|
70
|
+
return field.mandatory === true
|
|
71
|
+
? featureError(field, "error.400.feature_mandatory_missing")
|
|
72
|
+
: // An optional field left blank is complete, and no later rule may
|
|
73
|
+
// fire on an absent value — a `minLength` must not turn "did not
|
|
74
|
+
// answer" into "answered too short".
|
|
75
|
+
undefined;
|
|
76
|
+
}
|
|
77
|
+
// ── choices ────────────────────────────────────────────────────────────
|
|
78
|
+
const options = optionValues(field);
|
|
79
|
+
if (options !== undefined && config["allowCustom"] !== true) {
|
|
80
|
+
const answers = Array.isArray(value) ? value : [value];
|
|
81
|
+
const stray = answers.find((answer) => !options.some((option) => option === answer));
|
|
82
|
+
if (stray !== undefined) {
|
|
83
|
+
return featureError(field, "error.400.feature_not_in_options", {
|
|
84
|
+
ref_value: stray,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ── multi-select cardinality ───────────────────────────────────────────
|
|
89
|
+
if (field.kind === "select") {
|
|
90
|
+
const count = Array.isArray(value) ? value.length : 1;
|
|
91
|
+
const minSelected = num(config, "minSelected");
|
|
92
|
+
const maxSelected = num(config, "maxSelected");
|
|
93
|
+
if (minSelected !== undefined && count < minSelected) {
|
|
94
|
+
return featureError(field, "error.400.feature_below_minimum", {
|
|
95
|
+
min: minSelected,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (maxSelected !== undefined && count > maxSelected) {
|
|
99
|
+
return featureError(field, "error.400.feature_above_maximum", {
|
|
100
|
+
max: maxSelected,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
// ── numeric range ──────────────────────────────────────────────────────
|
|
106
|
+
if (field.kind === "int" || field.kind === "float") {
|
|
107
|
+
const asNumber = typeof value === "number" ? value : Number(value);
|
|
108
|
+
if (!Number.isFinite(asNumber)) {
|
|
109
|
+
return featureError(field, "error.400.feature_invalid_type");
|
|
110
|
+
}
|
|
111
|
+
if (field.kind === "int" && !Number.isInteger(asNumber)) {
|
|
112
|
+
return featureError(field, "error.400.feature_invalid_type");
|
|
113
|
+
}
|
|
114
|
+
const min = num(config, "min");
|
|
115
|
+
const max = num(config, "max");
|
|
116
|
+
if (min !== undefined && asNumber < min) {
|
|
117
|
+
return featureError(field, "error.400.feature_below_minimum", { min });
|
|
118
|
+
}
|
|
119
|
+
if (max !== undefined && asNumber > max) {
|
|
120
|
+
return featureError(field, "error.400.feature_above_maximum", { max });
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
// ── string length + pattern ────────────────────────────────────────────
|
|
125
|
+
if (field.kind === "string") {
|
|
126
|
+
const text = String(value);
|
|
127
|
+
const minLength = num(config, "minLength");
|
|
128
|
+
const maxLength = num(config, "maxLength");
|
|
129
|
+
if (minLength !== undefined && text.length < minLength) {
|
|
130
|
+
return featureError(field, "error.400.feature_below_minimum", {
|
|
131
|
+
min_length: minLength,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (maxLength !== undefined && text.length > maxLength) {
|
|
135
|
+
return featureError(field, "error.400.feature_above_maximum", {
|
|
136
|
+
max_length: maxLength,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const pattern = config["pattern"];
|
|
140
|
+
if (typeof pattern === "string" && pattern.length > 0) {
|
|
141
|
+
let re;
|
|
142
|
+
try {
|
|
143
|
+
re = new RegExp(pattern);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// An admin-authored pattern that JS cannot compile (a Python-only
|
|
147
|
+
// construct) is NOT the respondent's problem: skip the mirror and
|
|
148
|
+
// let the server, which compiled it, be the one to refuse.
|
|
149
|
+
re = undefined;
|
|
150
|
+
}
|
|
151
|
+
if (re !== undefined && !re.test(text)) {
|
|
152
|
+
return featureError(field, "error.400.feature_invalid_format");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate a whole answer set. Returns `{slug: FlowError}` for the fields that
|
|
161
|
+
* failed — the exact shape `FormFillBag.fieldErrors` carries, so a server
|
|
162
|
+
* response can be merged into it without a translation step.
|
|
163
|
+
*/
|
|
164
|
+
export function validateAnswers(fields, values) {
|
|
165
|
+
const errors = {};
|
|
166
|
+
for (const field of fields) {
|
|
167
|
+
const error = validateFieldValue(field, values[field.slug]);
|
|
168
|
+
if (error !== undefined)
|
|
169
|
+
errors[field.slug] = error;
|
|
170
|
+
}
|
|
171
|
+
return errors;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/widgets/validate.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# @stapel/forms-react 0.1.0
|
|
2
|
+
|
|
3
|
+
Headless React flow pair for stapel-forms (contract >=0.2 <0.3) — business + state, zero visual opinion.
|
|
4
|
+
Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
|
|
5
|
+
verification-403 interception, i18n engine, analytics facade, TanStack Query layer.
|
|
6
|
+
|
|
7
|
+
## The one right way (do this, the rest is a review/lint smell)
|
|
8
|
+
- No raw fetch/axios. The client is injected via <FormsProvider>/StapelConfigProvider;
|
|
9
|
+
every hook and flow already carries auth, refresh, and the error envelope.
|
|
10
|
+
- Render errors, never try/catch them: a flow's state carries FlowError{code,params};
|
|
11
|
+
render `t(code, params)` and branch on `explainFormsError(code)` remediation.
|
|
12
|
+
- Server state = the use* hooks (query layer); keys come only from formsQueryKeys.
|
|
13
|
+
- User strings = i18n keys (registerFormsI18n); never string literals.
|
|
14
|
+
- Sign-in UI = a headless flow component; copy it (shadcn-style) to restyle.
|
|
15
|
+
|
|
16
|
+
## Layers
|
|
17
|
+
api (typed client) · model (hooks, session) · flows (machines) · headless · i18n
|
|
18
|
+
|
|
19
|
+
## Documented flows (flows.json — canonical id, steps, endpoints)
|
|
20
|
+
|
|
21
|
+
## Operations (typed; use the named op, never a path string)
|
|
22
|
+
Request/response schema names are in manifest.json + the generated types.
|
|
23
|
+
Paths are relative to `/forms/api/v1/`.
|
|
24
|
+
- forms_api_v1_field_kinds_retrieve: GET /field-kinds
|
|
25
|
+
- forms_api_v1_forms_create: POST /forms
|
|
26
|
+
- forms_api_v1_forms_destroy: DELETE /forms/{form_id}
|
|
27
|
+
- forms_api_v1_forms_draft_update: PUT /forms/{form_id}/draft
|
|
28
|
+
- forms_api_v1_forms_list: GET /forms
|
|
29
|
+
- forms_api_v1_forms_partial_update: PATCH /forms/{form_id}
|
|
30
|
+
- forms_api_v1_forms_publish_create: POST /forms/{form_id}/publish
|
|
31
|
+
- forms_api_v1_forms_retrieve: GET /forms/{form_id}
|
|
32
|
+
- forms_api_v1_forms_rotate_link_create: POST /forms/{form_id}/rotate-link
|
|
33
|
+
- forms_api_v1_forms_state_create: POST /forms/{form_id}/state
|
|
34
|
+
- forms_api_v1_forms_submissions_export_retrieve: GET /forms/{form_id}/submissions/export
|
|
35
|
+
- forms_api_v1_forms_submissions_list: GET /forms/{form_id}/submissions
|
|
36
|
+
- forms_api_v1_forms_versions_list: GET /forms/{form_id}/versions
|
|
37
|
+
- forms_api_v1_public_retrieve: GET /public/{public_id}/
|
|
38
|
+
- forms_api_v1_public_submissions_create: POST /public/{public_id}/submissions/
|
|
39
|
+
- forms_api_v1_submissions_destroy: DELETE /submissions/{submission_id}
|
|
40
|
+
- forms_api_v1_submissions_resend_create: POST /submissions/{submission_id}/resend
|
|
41
|
+
- forms_api_v1_submissions_retrieve: GET /submissions/{submission_id}
|
|
42
|
+
|
|
43
|
+
## Query hooks (server state; keys come only from the key factory)
|
|
44
|
+
- useCreateForm (mutation) → createForm
|
|
45
|
+
- useDeleteForm (mutation) → deleteForm
|
|
46
|
+
- useDeleteSubmission (mutation) → deleteSubmission
|
|
47
|
+
- useFieldKinds (query) → listFieldKinds
|
|
48
|
+
- useForm (query) → getForm
|
|
49
|
+
- useForms (query) → listForms
|
|
50
|
+
- useFormVersions (query) → listVersions
|
|
51
|
+
- usePublicForm (query) → getPublicForm
|
|
52
|
+
- usePublishForm (mutation) → publish
|
|
53
|
+
- useResendSubmission (mutation) → resendSubmission
|
|
54
|
+
- useRotateLink (mutation) → rotateLink
|
|
55
|
+
- useSaveDraft (mutation) → putDraft
|
|
56
|
+
- useSetFormState (mutation) → setState
|
|
57
|
+
- useSubmission (query) → getSubmission
|
|
58
|
+
- useSubmissions (query) → listSubmissions
|
|
59
|
+
- useSubmitForm (mutation) → submit
|
|
60
|
+
- useUpdateForm (mutation) → patchForm
|
|
61
|
+
|
|
62
|
+
## Errors (render t(code, params); UX from remediation)
|
|
63
|
+
75 keys (full catalog: manifest.json §errors). By remediation: fix_input 41 · retry 14 · verify 7 · contact_support 6 · wait_and_retry 6 · reauthenticate 1.
|
|
64
|
+
Param-bearing keys (interpolation slots matter):
|
|
65
|
+
- error.400.description_too_long [400] → fix_input {max_length}
|
|
66
|
+
- error.400.description_too_short [400] → fix_input {min_length}
|
|
67
|
+
- error.400.feature_above_maximum [400] → fix_input {feature}
|
|
68
|
+
- error.400.feature_below_minimum [400] → fix_input {feature}
|
|
69
|
+
- error.400.feature_invalid_config [400] → fix_input {feature}
|
|
70
|
+
- error.400.feature_invalid_format [400] → fix_input {feature}
|
|
71
|
+
- error.400.feature_invalid_type [400] → fix_input {feature}
|
|
72
|
+
- error.400.feature_mandatory_missing [400] → fix_input {feature}
|
|
73
|
+
- error.400.feature_not_allowed [400] → fix_input {feature}
|
|
74
|
+
- error.400.feature_not_in_options [400] → fix_input {feature}
|
|
75
|
+
- error.400.feature_unknown [400] → fix_input {feature}
|
|
76
|
+
- error.400.feature_unknown_type [400] → fix_input {feature}
|
|
77
|
+
- error.400.field.blank [400] → fix_input {field}
|
|
78
|
+
- error.400.field.does_not_exist [400] → fix_input {field}
|
|
79
|
+
- error.400.field.invalid [400] → fix_input {field}
|
|
80
|
+
- error.400.field.invalid_choice [400] → fix_input {field}
|
|
81
|
+
- error.400.field.max_length [400] → fix_input {field,max_length}
|
|
82
|
+
- error.400.field.max_value [400] → fix_input {field,max_value}
|
|
83
|
+
- error.400.field.min_length [400] → fix_input {field,min_length}
|
|
84
|
+
- error.400.field.min_value [400] → fix_input {field,min_value}
|
|
85
|
+
- error.400.field.null [400] → fix_input {field}
|
|
86
|
+
- error.400.field.required [400] → fix_input {field}
|
|
87
|
+
- error.400.field.unique [400] → fix_input {field}
|
|
88
|
+
- error.400.forms_duplicate_slug [400] → fix_input {slug}
|
|
89
|
+
- error.400.forms_kind_not_allowed [400] → fix_input {kind}
|
|
90
|
+
- error.429.rate_limit [429] → wait_and_retry {retry_after_minutes}
|
|
91
|
+
|
|
92
|
+
## Analytics events (typed; defineEvent → events.json, drift-gated)
|
|
93
|
+
- (no app defineEvent() in this pair — its analytic events are the
|
|
94
|
+
auto-instrumented flow funnels below)
|
|
95
|
+
|
|
96
|
+
## Flow funnels (auto-instrumented: flow.<id>.<step> {phase})
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
// Typed event + tracked() click (the one right way; §3.1).
|
|
100
|
+
const planSelected = defineEvent({
|
|
101
|
+
name: "pricing.plan.selected",
|
|
102
|
+
description: "User picked a plan",
|
|
103
|
+
props: { plan: prop.oneOf(["free", "pro", "team"], "Plan code") },
|
|
104
|
+
});
|
|
105
|
+
const { tracked } = useTracked();
|
|
106
|
+
<Button onClick={tracked(planSelected, { plan }, startCheckout)} />
|
|
107
|
+
// A click that STEPS a flow machine is already instrumented — mark it
|
|
108
|
+
// data-analytics="flow" instead; tracked() on top double-counts (§3.2).
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Demos (defineDemo → manifest.demos; compiled, linted, rendered examples)
|
|
112
|
+
- forms.builder → <FormBuilder> [default] demo/FormBuilder.demo.tsx
|
|
113
|
+
- forms.fill → <FormFill> [default|closed|outage] demo/FormFill.demo.tsx
|
|
114
|
+
- forms.list → <FormList> [default] demo/FormList.demo.tsx
|
|
115
|
+
- forms.provider → <FormsProvider> [default] demo/Forms.demo.tsx
|
|
116
|
+
- forms.responses → <ResponsesTable> [default|empty] demo/ResponsesTable.demo.tsx
|
|
117
|
+
Each source file is the canonical usage snippet (open the default variant).
|
|
118
|
+
|
|
119
|
+
## Snippets
|
|
120
|
+
```tsx
|
|
121
|
+
// Error rendering + remediation branch (one pattern for every pair).
|
|
122
|
+
const r = explainFormsError(err.code); // 'wait_and_retry' | 'verify' | ...
|
|
123
|
+
return <Alert action={r}>{t(err.code, err.params)}</Alert>;
|
|
124
|
+
```
|