@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,279 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { actionAvailable, actionBlocked, hasErrorCode, isStapelApiError, loadStateFromQuery, requireLoaded, } from "@stapel/core";
|
|
3
|
+
import { usePublicForm } from "../model/queries.js";
|
|
4
|
+
import { useSubmitForm } from "../model/mutations.js";
|
|
5
|
+
import { resolveFormFieldWidget } from "../widgets/registry.js";
|
|
6
|
+
import { isBlank, validateAnswers } from "../widgets/validate.js";
|
|
7
|
+
import { FORMS_I18N_KEYS } from "../i18n/keys.js";
|
|
8
|
+
/** `error.409.forms_version_superseded` — the schema moved while the person
|
|
9
|
+
* was filling it in. */
|
|
10
|
+
const CODE_SUPERSEDED = "error.409.forms_version_superseded";
|
|
11
|
+
/** The map without one key, or the SAME object when the key was not there —
|
|
12
|
+
* identity matters: returning a fresh object on every keystroke would
|
|
13
|
+
* re-render every field that reads `fieldErrors`. */
|
|
14
|
+
function withoutKey(map, slug) {
|
|
15
|
+
if (map[slug] === undefined)
|
|
16
|
+
return map;
|
|
17
|
+
return Object.fromEntries(Object.entries(map).filter(([key]) => key !== slug));
|
|
18
|
+
}
|
|
19
|
+
/** True when the widget layer can draw this field at all. */
|
|
20
|
+
function isRenderable(field, builtinKinds) {
|
|
21
|
+
return (resolveFormFieldWidget(field.kind) !== null || builtinKinds.has(field.kind));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read the per-field entries out of a server refusal.
|
|
25
|
+
*
|
|
26
|
+
* The wire shape (stapel-forms `views._maps_forms_errors`) puts the FIRST
|
|
27
|
+
* field error's code at the top level and the whole set under
|
|
28
|
+
* `params.fields[]` as `{field, code, params}`. Both are read: the array when
|
|
29
|
+
* present, otherwise the top-level `params.field`, so a single-field refusal
|
|
30
|
+
* still lands on its control.
|
|
31
|
+
*/
|
|
32
|
+
function serverFieldErrors(error) {
|
|
33
|
+
const out = {};
|
|
34
|
+
const rows = error.params["fields"];
|
|
35
|
+
if (Array.isArray(rows)) {
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
if (row === null || typeof row !== "object")
|
|
38
|
+
continue;
|
|
39
|
+
const entry = { ...row };
|
|
40
|
+
const field = entry["field"];
|
|
41
|
+
const code = entry["code"];
|
|
42
|
+
if (typeof field !== "string" || typeof code !== "string")
|
|
43
|
+
continue;
|
|
44
|
+
const params = entry["params"];
|
|
45
|
+
out[field] = {
|
|
46
|
+
code,
|
|
47
|
+
params: params !== null && typeof params === "object"
|
|
48
|
+
? { ...params }
|
|
49
|
+
: {},
|
|
50
|
+
status: error.status,
|
|
51
|
+
message: undefined,
|
|
52
|
+
language: undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const single = error.params["field"];
|
|
57
|
+
if (Object.keys(out).length === 0 && typeof single === "string") {
|
|
58
|
+
out[single] = {
|
|
59
|
+
code: error.code,
|
|
60
|
+
params: error.params,
|
|
61
|
+
status: error.status,
|
|
62
|
+
message: error.message,
|
|
63
|
+
language: error.language,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/** Answers worth sending: blanks are omitted (an unanswered optional field is
|
|
69
|
+
* not an empty answer), and `header` fields are never sent — the engine
|
|
70
|
+
* regenerates their DAO from config and stapel-forms rejects an answer to one
|
|
71
|
+
* outright (backend delta note 1). */
|
|
72
|
+
function answersToSend(fields, values) {
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const field of fields) {
|
|
75
|
+
if (field.kind === "header")
|
|
76
|
+
continue;
|
|
77
|
+
const value = values[field.slug];
|
|
78
|
+
if (isBlank(value))
|
|
79
|
+
continue;
|
|
80
|
+
out[field.slug] = value;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/** Seed the answers a schema declares a value default for. Only `date`
|
|
85
|
+
* declares one upstream (`config.default`); seeding it matches what the
|
|
86
|
+
* engine would have applied, so the person sees the same value the server
|
|
87
|
+
* would have stored. */
|
|
88
|
+
function seedValues(fields) {
|
|
89
|
+
const seeded = {};
|
|
90
|
+
for (const field of fields) {
|
|
91
|
+
const declared = (field.config ?? {})["default"];
|
|
92
|
+
if (field.kind === "date" && declared !== undefined && declared !== null) {
|
|
93
|
+
seeded[field.slug] = declared;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return seeded;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Headless form fill — the anonymous respondent's whole surface, renderless.
|
|
100
|
+
*
|
|
101
|
+
* ```tsx
|
|
102
|
+
* <FormFill publicId="k3J…x9">
|
|
103
|
+
* {(bag) => matchLoad(bag.state, { loading, failed, ready })}
|
|
104
|
+
* </FormFill>
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* The `/default` skin's `<StapelForm>` is one renderer over this bag; a host
|
|
108
|
+
* that wants its own visuals writes another and loses nothing.
|
|
109
|
+
*/
|
|
110
|
+
export function FormFill(props) {
|
|
111
|
+
const query = usePublicForm(props.publicId);
|
|
112
|
+
const submitMutation = useSubmitForm();
|
|
113
|
+
const state = loadStateFromQuery(query);
|
|
114
|
+
const [values, setValues] = useState({});
|
|
115
|
+
const [clientErrors, setClientErrors] = useState({});
|
|
116
|
+
const [serverErrors, setServerErrors] = useState({});
|
|
117
|
+
const [formError, setFormError] = useState(null);
|
|
118
|
+
const [submitted, setSubmitted] = useState(null);
|
|
119
|
+
const [superseded, setSuperseded] = useState(false);
|
|
120
|
+
const [captchaToken, setCaptchaTokenState] = useState(null);
|
|
121
|
+
const form = state.status === "ready" ? state.data : undefined;
|
|
122
|
+
const fields = useMemo(() => form?.fields ?? [], [form]);
|
|
123
|
+
const builtinKinds = useMemo(() => new Set(props.builtinKinds ?? []), [props.builtinKinds]);
|
|
124
|
+
// Seed declared defaults once per version. Keyed on `version_id` so a
|
|
125
|
+
// supersede re-seeds against the NEW schema rather than leaving a default
|
|
126
|
+
// the new version no longer declares.
|
|
127
|
+
const seededVersion = useRef(null);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (form === undefined)
|
|
130
|
+
return;
|
|
131
|
+
if (seededVersion.current === form.version_id)
|
|
132
|
+
return;
|
|
133
|
+
const previous = seededVersion.current;
|
|
134
|
+
seededVersion.current = form.version_id;
|
|
135
|
+
if (previous === null) {
|
|
136
|
+
const seeded = seedValues(form.fields);
|
|
137
|
+
if (Object.keys(seeded).length > 0) {
|
|
138
|
+
setValues((current) => ({ ...seeded, ...current }));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// A NEW version arrived under a filled-in form — the 409 refetch path.
|
|
143
|
+
// Keep an answer only where the slug still exists AND still has the same
|
|
144
|
+
// kind: a slug reused for a different kind is a different question, and
|
|
145
|
+
// carrying the old answer over would submit an answer to a question
|
|
146
|
+
// nobody was asked. Everything else is dropped, visibly, and `superseded`
|
|
147
|
+
// makes the skin say so.
|
|
148
|
+
const compatible = new Map(form.fields.map((f) => [f.slug, f.kind]));
|
|
149
|
+
setValues((current) => {
|
|
150
|
+
const kept = {};
|
|
151
|
+
for (const [slug, value] of Object.entries(current)) {
|
|
152
|
+
if (compatible.get(slug) !== undefined)
|
|
153
|
+
kept[slug] = value;
|
|
154
|
+
}
|
|
155
|
+
return kept;
|
|
156
|
+
});
|
|
157
|
+
setClientErrors({});
|
|
158
|
+
setServerErrors({});
|
|
159
|
+
}, [form]);
|
|
160
|
+
const setValue = useCallback((slug, value) => {
|
|
161
|
+
setValues((current) => ({ ...current, [slug]: value }));
|
|
162
|
+
// Clearing on edit is the point of a mirror: the person changed the
|
|
163
|
+
// thing that was wrong, so the old refusal is stale on both sides.
|
|
164
|
+
setClientErrors((current) => withoutKey(current, slug));
|
|
165
|
+
setServerErrors((current) => withoutKey(current, slug));
|
|
166
|
+
}, []);
|
|
167
|
+
const setCaptchaToken = useCallback((token) => {
|
|
168
|
+
setCaptchaTokenState(token);
|
|
169
|
+
}, []);
|
|
170
|
+
const unsupportedKinds = useMemo(() => {
|
|
171
|
+
const kinds = new Set();
|
|
172
|
+
for (const field of fields) {
|
|
173
|
+
if (!isRenderable(field, builtinKinds))
|
|
174
|
+
kinds.add(field.kind);
|
|
175
|
+
}
|
|
176
|
+
return [...kinds].sort();
|
|
177
|
+
}, [fields, builtinKinds]);
|
|
178
|
+
const fieldErrors = useMemo(() => ({ ...clientErrors, ...serverErrors }), [clientErrors, serverErrors]);
|
|
179
|
+
const isSubmitting = submitMutation.isPending;
|
|
180
|
+
const submit = useMemo(() => {
|
|
181
|
+
if (submitted !== null) {
|
|
182
|
+
return actionBlocked(FORMS_I18N_KEYS.submitBlockedDone);
|
|
183
|
+
}
|
|
184
|
+
return requireLoaded(state, () => {
|
|
185
|
+
if (unsupportedKinds.length > 0) {
|
|
186
|
+
return actionBlocked(FORMS_I18N_KEYS.submitBlockedUnsupported, {
|
|
187
|
+
kinds: unsupportedKinds.join(", "),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (isSubmitting) {
|
|
191
|
+
return actionBlocked(FORMS_I18N_KEYS.submitBlockedInFlight);
|
|
192
|
+
}
|
|
193
|
+
return actionAvailable();
|
|
194
|
+
});
|
|
195
|
+
}, [state, submitted, unsupportedKinds, isSubmitting]);
|
|
196
|
+
const refetch = useCallback(() => {
|
|
197
|
+
void query.refetch();
|
|
198
|
+
}, [query]);
|
|
199
|
+
const doSubmit = useCallback(() => {
|
|
200
|
+
if (form === undefined || submitted !== null || isSubmitting)
|
|
201
|
+
return;
|
|
202
|
+
const mirrored = validateAnswers(fields, values);
|
|
203
|
+
setServerErrors({});
|
|
204
|
+
setFormError(null);
|
|
205
|
+
if (Object.keys(mirrored).length > 0) {
|
|
206
|
+
setClientErrors(mirrored);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
setClientErrors({});
|
|
210
|
+
submitMutation.mutate({
|
|
211
|
+
publicId: props.publicId,
|
|
212
|
+
body: {
|
|
213
|
+
answers: answersToSend(fields, values),
|
|
214
|
+
// Echoing the rendered version is what turns a racing publish into
|
|
215
|
+
// a clean 409 instead of a silent mis-validation against a schema
|
|
216
|
+
// the person never saw.
|
|
217
|
+
version_id: form.version_id,
|
|
218
|
+
...(captchaToken !== null ? { captcha_token: captchaToken } : {}),
|
|
219
|
+
},
|
|
220
|
+
}, {
|
|
221
|
+
onSuccess: (result) => {
|
|
222
|
+
const confirmation = result.confirmation ??
|
|
223
|
+
form.meta.confirmation_text ??
|
|
224
|
+
"";
|
|
225
|
+
setSubmitted({ confirmation });
|
|
226
|
+
setSuperseded(false);
|
|
227
|
+
props.onSubmitted?.({ confirmation });
|
|
228
|
+
},
|
|
229
|
+
onError: (caught) => {
|
|
230
|
+
if (!isStapelApiError(caught)) {
|
|
231
|
+
setFormError(null);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (hasErrorCode(caught, CODE_SUPERSEDED)) {
|
|
235
|
+
// Refetch and let the seeding effect preserve what still fits.
|
|
236
|
+
// The token is dropped: a captcha is spent, and replaying it
|
|
237
|
+
// against the next attempt would fail on the server's terms.
|
|
238
|
+
setSuperseded(true);
|
|
239
|
+
setCaptchaTokenState(null);
|
|
240
|
+
refetch();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const perField = serverFieldErrors(caught);
|
|
244
|
+
if (Object.keys(perField).length > 0) {
|
|
245
|
+
setServerErrors(perField);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
setFormError(caught);
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}, [
|
|
252
|
+
form,
|
|
253
|
+
fields,
|
|
254
|
+
values,
|
|
255
|
+
captchaToken,
|
|
256
|
+
isSubmitting,
|
|
257
|
+
submitted,
|
|
258
|
+
submitMutation,
|
|
259
|
+
props,
|
|
260
|
+
refetch,
|
|
261
|
+
]);
|
|
262
|
+
return props.children({
|
|
263
|
+
state,
|
|
264
|
+
values,
|
|
265
|
+
setValue,
|
|
266
|
+
fieldErrors,
|
|
267
|
+
submit,
|
|
268
|
+
doSubmit,
|
|
269
|
+
isSubmitting,
|
|
270
|
+
submitted,
|
|
271
|
+
formError,
|
|
272
|
+
superseded,
|
|
273
|
+
unsupportedKinds,
|
|
274
|
+
setCaptchaToken,
|
|
275
|
+
captchaToken,
|
|
276
|
+
refetch,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=FormFill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormFill.js","sourceRoot":"","sources":["../../src/headless/FormFill.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { LoadState, StapelApiError } from "@stapel/core";
|
|
3
|
+
import type { FormCreateRequest, FormRow, FormState } from "../api/types.js";
|
|
4
|
+
/** The bag `<FormList>` hands its render prop. */
|
|
5
|
+
export interface FormListBag {
|
|
6
|
+
/**
|
|
7
|
+
* The workspace's forms as a state a skin cannot flatten. Render with
|
|
8
|
+
* core's `matchList` — its four required arms are what keeps "no forms yet"
|
|
9
|
+
* a sentence that can only be said about a load that actually succeeded.
|
|
10
|
+
*/
|
|
11
|
+
readonly state: LoadState<readonly FormRow[]>;
|
|
12
|
+
/** The active state filter, or `null` for all. */
|
|
13
|
+
readonly filter: FormState | null;
|
|
14
|
+
setFilter(state: FormState | null): void;
|
|
15
|
+
/** `forms.manage`. */
|
|
16
|
+
create(input: Omit<FormCreateRequest, "workspace_id">): void;
|
|
17
|
+
readonly isCreating: boolean;
|
|
18
|
+
/** The last create refusal — `error.400.forms_too_many_open` carries the
|
|
19
|
+
* workspace's cap in `params.limit`. */
|
|
20
|
+
readonly error: StapelApiError | null;
|
|
21
|
+
refetch(): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Headless list of a workspace's forms — the admin surface's entry point,
|
|
25
|
+
* renderless.
|
|
26
|
+
*/
|
|
27
|
+
export declare function FormList(props: {
|
|
28
|
+
workspaceId: string;
|
|
29
|
+
/** Called with the new form once the server creates it, so a skin can
|
|
30
|
+
* navigate straight into the builder. */
|
|
31
|
+
onCreated?: (form: FormRow) => void;
|
|
32
|
+
children: (bag: FormListBag) => ReactNode;
|
|
33
|
+
}): ReactNode;
|
|
34
|
+
//# sourceMappingURL=FormList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormList.d.ts","sourceRoot":"","sources":["../../src/headless/FormList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI7E,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;IAC9C,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACzC,sBAAsB;IACtB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;IAC7D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;4CACwC;IACxC,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB;6CACyC;IACzC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,SAAS,CAAC;CAC3C,GAAG,SAAS,CAmCZ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { isStapelApiError, loadStateFromQuery } from "@stapel/core";
|
|
3
|
+
import { useForms } from "../model/queries.js";
|
|
4
|
+
import { useCreateForm } from "../model/mutations.js";
|
|
5
|
+
/**
|
|
6
|
+
* Headless list of a workspace's forms — the admin surface's entry point,
|
|
7
|
+
* renderless.
|
|
8
|
+
*/
|
|
9
|
+
export function FormList(props) {
|
|
10
|
+
const [filter, setFilter] = useState(null);
|
|
11
|
+
const [error, setError] = useState(null);
|
|
12
|
+
const query = useForms(props.workspaceId, filter ?? undefined);
|
|
13
|
+
const createMutation = useCreateForm();
|
|
14
|
+
const create = useCallback((input) => {
|
|
15
|
+
setError(null);
|
|
16
|
+
createMutation.mutate({ ...input, workspace_id: props.workspaceId }, {
|
|
17
|
+
onError: (caught) => {
|
|
18
|
+
setError(isStapelApiError(caught) ? caught : null);
|
|
19
|
+
},
|
|
20
|
+
onSuccess: (form) => {
|
|
21
|
+
props.onCreated?.(form);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}, [createMutation, props]);
|
|
25
|
+
return props.children({
|
|
26
|
+
state: loadStateFromQuery(query),
|
|
27
|
+
filter,
|
|
28
|
+
setFilter,
|
|
29
|
+
create,
|
|
30
|
+
isCreating: createMutation.isPending,
|
|
31
|
+
error,
|
|
32
|
+
refetch: () => {
|
|
33
|
+
void query.refetch();
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=FormList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormList.js","sourceRoot":"","sources":["../../src/headless/FormList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAsBtD;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,KAMxB;IACC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAmB,IAAI,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAwB,IAAI,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;IAC/D,MAAM,cAAc,GAAG,aAAa,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,KAA8C,EAAQ,EAAE;QACvD,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,cAAc,CAAC,MAAM,CACnB,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,WAAW,EAAE,EAC7C;YACE,OAAO,EAAE,CAAC,MAAe,EAAE,EAAE;gBAC3B,QAAQ,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACrD,CAAC;YACD,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;gBAClB,KAAK,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;SACF,CACF,CAAC;IACJ,CAAC,EACD,CAAC,cAAc,EAAE,KAAK,CAAC,CACxB,CAAC;IAEF,OAAO,KAAK,CAAC,QAAQ,CAAC;QACpB,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC;QAChC,MAAM;QACN,SAAS;QACT,MAAM;QACN,UAAU,EAAE,cAAc,CAAC,SAAS;QACpC,KAAK;QACL,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
import type { FormsRuntime } from "../model/runtime.js";
|
|
3
|
+
/**
|
|
4
|
+
* Provides the wired {@link FormsRuntime} to every forms hook and
|
|
5
|
+
* headless component below it. Bring your own visual shell — this component
|
|
6
|
+
* renders nothing of its own. (Core's `createModuleContext` provider, bound
|
|
7
|
+
* to this pair — slim wave §21/S2.)
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const runtime = createFormsRuntime({ baseUrl: "/forms/api/v1/" });
|
|
11
|
+
* // give runtime.client to core's <StapelConfigProvider config={{ client }}>
|
|
12
|
+
* <FormsProvider runtime={runtime}>{app}</FormsProvider>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare const FormsProvider: (props: {
|
|
16
|
+
runtime: FormsRuntime;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}) => ReactElement;
|
|
19
|
+
//# sourceMappingURL=FormsProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormsProvider.d.ts","sourceRoot":"","sources":["../../src/headless/FormsProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,CAAC,KAAK,EAAE;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACrB,KAAK,YAA6B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ModuleProvider } from "../model/context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Provides the wired {@link FormsRuntime} to every forms hook and
|
|
4
|
+
* headless component below it. Bring your own visual shell — this component
|
|
5
|
+
* renders nothing of its own. (Core's `createModuleContext` provider, bound
|
|
6
|
+
* to this pair — slim wave §21/S2.)
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const runtime = createFormsRuntime({ baseUrl: "/forms/api/v1/" });
|
|
10
|
+
* // give runtime.client to core's <StapelConfigProvider config={{ client }}>
|
|
11
|
+
* <FormsProvider runtime={runtime}>{app}</FormsProvider>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export const FormsProvider = ModuleProvider;
|
|
15
|
+
//# sourceMappingURL=FormsProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormsProvider.js","sourceRoot":"","sources":["../../src/headless/FormsProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAGL,cAAc,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ActionAvailability, LoadState, StapelApiError } from "@stapel/core";
|
|
3
|
+
import type { FormVersion, Submission } from "../api/types.js";
|
|
4
|
+
import type { ResendRequest } from "../api/types.js";
|
|
5
|
+
/** One column of the responses grid — a field of the version being reviewed. */
|
|
6
|
+
export interface ResponseColumn {
|
|
7
|
+
readonly slug: string;
|
|
8
|
+
/** The admin-authored label, or the slug when the field was never named. */
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly kind: string;
|
|
11
|
+
}
|
|
12
|
+
/** The rows and the columns that describe them, loaded together. */
|
|
13
|
+
export interface ResponsesView {
|
|
14
|
+
readonly rows: readonly Submission[];
|
|
15
|
+
/**
|
|
16
|
+
* Columns for the CURRENT version filter. Per-version column sets are what
|
|
17
|
+
* the version FK buys: a submission records which schema it answered, so
|
|
18
|
+
* reviewing an old response shows the questions that were actually asked,
|
|
19
|
+
* not today's.
|
|
20
|
+
*
|
|
21
|
+
* With no version filter this is the union of every version's fields, in
|
|
22
|
+
* newest-version-first order — the honest superset, because rows from
|
|
23
|
+
* different versions genuinely have different keys.
|
|
24
|
+
*/
|
|
25
|
+
readonly columns: readonly ResponseColumn[];
|
|
26
|
+
}
|
|
27
|
+
/** The bag `<ResponsesTable>` hands its render prop (spec §8.2). */
|
|
28
|
+
export interface ResponsesTableBag {
|
|
29
|
+
readonly state: LoadState<ResponsesView>;
|
|
30
|
+
/** Every published version, for the filter control. */
|
|
31
|
+
readonly versions: LoadState<readonly FormVersion[]>;
|
|
32
|
+
/** The active version filter, or `null` for "all versions". */
|
|
33
|
+
readonly version: number | null;
|
|
34
|
+
setVersion(version: number | null): void;
|
|
35
|
+
/** Go to the next keyset page. Blocked with a reason at the end of the
|
|
36
|
+
* list, while loading, and when the load failed. */
|
|
37
|
+
readonly nextPage: ActionAvailability;
|
|
38
|
+
goNextPage(): void;
|
|
39
|
+
/** Go back one page. Blocked with a reason on the first page. */
|
|
40
|
+
readonly prevPage: ActionAvailability;
|
|
41
|
+
goPrevPage(): void;
|
|
42
|
+
/** 0-based index of the page on screen. */
|
|
43
|
+
readonly pageIndex: number;
|
|
44
|
+
/** The row opened in a detail drawer, or `null`. */
|
|
45
|
+
readonly selected: Submission | null;
|
|
46
|
+
select(submission: Submission | null): void;
|
|
47
|
+
/** `forms.responses.manage`. */
|
|
48
|
+
remove(submissionId: string): void;
|
|
49
|
+
readonly isRemoving: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Re-deliver one response. An `override` REPLACES the form's configured
|
|
52
|
+
* targets rather than adding to them (backend delta note 7).
|
|
53
|
+
*/
|
|
54
|
+
resend(submissionId: string, override?: ResendRequest): void;
|
|
55
|
+
readonly isResending: boolean;
|
|
56
|
+
/** How many deliveries the last resend reported, or `null`. */
|
|
57
|
+
readonly lastResendCount: number | null;
|
|
58
|
+
/** Drive the CSV export to completion; resolves to the whole file's text. */
|
|
59
|
+
exportCsv(): Promise<string>;
|
|
60
|
+
readonly isExporting: boolean;
|
|
61
|
+
readonly exportPages: number;
|
|
62
|
+
/** The last mutation refusal (delete / resend / export). */
|
|
63
|
+
readonly error: StapelApiError | null;
|
|
64
|
+
refetch(): void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Headless response review — keyset paging, per-version columns, detail
|
|
68
|
+
* selection, delete, resend and CSV export, renderless.
|
|
69
|
+
*
|
|
70
|
+
* Live-updating counts are refetch-only by design: a `forms:ws:<workspace_id>`
|
|
71
|
+
* Signal stream is RESERVED naming for when the stapel-realtime substrate
|
|
72
|
+
* lands (spec §8.2), and forms does not build a socket of its own — that is a
|
|
73
|
+
* lint boundary, not an omission.
|
|
74
|
+
*/
|
|
75
|
+
export declare function ResponsesTable(props: {
|
|
76
|
+
workspaceId: string;
|
|
77
|
+
formId: string;
|
|
78
|
+
/** Rows per page. Default 50. */
|
|
79
|
+
limit?: number;
|
|
80
|
+
children: (bag: ResponsesTableBag) => ReactNode;
|
|
81
|
+
}): ReactNode;
|
|
82
|
+
//# sourceMappingURL=ResponsesTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResponsesTable.d.ts","sourceRoot":"","sources":["../../src/headless/ResponsesTable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvC,OAAO,KAAK,EACV,kBAAkB,EAClB,SAAS,EACT,cAAc,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAEV,WAAW,EACX,UAAU,EACX,MAAM,iBAAiB,CAAC;AAOzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAMrD,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAC;IACrC;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;CAC7C;AAED,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;IACrD,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAEzC;wDACoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,UAAU,IAAI,IAAI,CAAC;IACnB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,UAAU,IAAI,IAAI,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IAE5C,gCAAgC;IAChC,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC,6EAA6E;IAC7E,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,SAAS,CAAC;CACjD,GAAG,SAAS,CAmMZ"}
|