@10x-media/form-builder 0.1.0-beta.4 → 0.1.0-beta.5
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 +83 -0
- package/README.md +3 -3
- package/dist/actions/body/serializeBody.d.ts +8 -5
- package/dist/actions/body/serializeBody.js.map +1 -1
- package/dist/actions/body/textOfBody.d.ts +13 -0
- package/dist/actions/body/textOfBody.js +35 -0
- package/dist/actions/body/textOfBody.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +41 -11
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +77 -38
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +8 -3
- package/dist/actions/builtin/index.js +8 -5
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/defineAction.d.ts +1 -1
- package/dist/actions/fromAddresses.d.ts +21 -0
- package/dist/actions/fromAddresses.js +74 -0
- package/dist/actions/fromAddresses.js.map +1 -0
- package/dist/actions/task.js +9 -8
- package/dist/actions/task.js.map +1 -1
- package/dist/aggregation/aggregateResponses.d.ts +9 -0
- package/dist/aggregation/aggregateResponses.js +8 -14
- package/dist/aggregation/aggregateResponses.js.map +1 -1
- package/dist/aggregation/resolveResultsRequest.d.ts +19 -6
- package/dist/aggregation/resolveResultsRequest.js +58 -12
- package/dist/aggregation/resolveResultsRequest.js.map +1 -1
- package/dist/calc/computeCalcFields.js +2 -1
- package/dist/calc/computeCalcFields.js.map +1 -1
- package/dist/client/ByteSizeField.d.ts +8 -0
- package/dist/client/ByteSizeField.js +3 -1
- package/dist/client/ByteSizeField.js.map +1 -1
- package/dist/client/ClosePollButton.d.ts +15 -0
- package/dist/client/ClosePollButton.js +80 -0
- package/dist/client/ClosePollButton.js.map +1 -0
- package/dist/client/CondensedArray.d.ts +15 -0
- package/dist/client/CondensedArray.js +97 -0
- package/dist/client/CondensedArray.js.map +1 -0
- package/dist/client/EndpointOptionsSelect.d.ts +45 -0
- package/dist/client/EndpointOptionsSelect.js +103 -0
- package/dist/client/EndpointOptionsSelect.js.map +1 -0
- package/dist/client/FieldBlockLabel.d.ts +18 -0
- package/dist/client/FieldBlockLabel.js +34 -0
- package/dist/client/FieldBlockLabel.js.map +1 -0
- package/dist/client/FieldNameSelect.d.ts +20 -4
- package/dist/client/FieldNameSelect.js +25 -14
- package/dist/client/FieldNameSelect.js.map +1 -1
- package/dist/client/FlowBuilder.d.ts +2 -1
- package/dist/client/FlowBuilder.js +63 -57
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/RuleDescription.d.ts +21 -0
- package/dist/client/RuleDescription.js +19 -0
- package/dist/client/RuleDescription.js.map +1 -0
- package/dist/client/admin.css +36 -0
- package/dist/client/endpointOptions.js +29 -0
- package/dist/client/endpointOptions.js.map +1 -0
- package/dist/client/flowAuthoring.js +114 -7
- package/dist/client/flowAuthoring.js.map +1 -1
- package/dist/client/synthesizeClientField.js +15 -8
- package/dist/client/synthesizeClientField.js.map +1 -1
- package/dist/collections/buttonFields.d.ts +34 -0
- package/dist/collections/buttonFields.js +35 -0
- package/dist/collections/buttonFields.js.map +1 -0
- package/dist/collections/cells/FieldCountCell.d.ts +15 -0
- package/dist/collections/cells/FieldCountCell.js +18 -0
- package/dist/collections/cells/FieldCountCell.js.map +1 -0
- package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
- package/dist/collections/cells/FlowStepsCell.js +17 -0
- package/dist/collections/cells/FlowStepsCell.js.map +1 -0
- package/dist/collections/formSubmissions.js +22 -17
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +310 -137
- package/dist/collections/forms.js.map +1 -1
- package/dist/collections/redirectFields.d.ts +29 -0
- package/dist/consent/applyConsentStatements.d.ts +14 -0
- package/dist/consent/applyConsentStatements.js +17 -0
- package/dist/consent/applyConsentStatements.js.map +1 -0
- package/dist/consent/captureConsent.d.ts +19 -8
- package/dist/consent/captureConsent.js +34 -11
- package/dist/consent/captureConsent.js.map +1 -1
- package/dist/consent/consentSourcesField.d.ts +58 -0
- package/dist/consent/consentSourcesField.js +79 -0
- package/dist/consent/consentSourcesField.js.map +1 -0
- package/dist/consent/resolveConsentEntries.d.ts +24 -0
- package/dist/consent/resolveConsentEntries.js +43 -0
- package/dist/consent/resolveConsentEntries.js.map +1 -0
- package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
- package/dist/consent/resolveConsentSourcesRequest.js +55 -0
- package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
- package/dist/consent/resolveConsentStatements.d.ts +45 -0
- package/dist/consent/resolveConsentStatements.js +44 -0
- package/dist/consent/resolveConsentStatements.js.map +1 -0
- package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
- package/dist/consent/resolvePublishedVersionRef.js +13 -10
- package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
- package/dist/consent/types.d.ts +49 -0
- package/dist/email/departments.d.ts +55 -0
- package/dist/email/departments.js +130 -0
- package/dist/email/departments.js.map +1 -0
- package/dist/email/departmentsField.d.ts +49 -0
- package/dist/email/departmentsField.js +69 -0
- package/dist/email/departmentsField.js.map +1 -0
- package/dist/exports/client.d.ts +8 -1
- package/dist/exports/client.js +8 -1
- package/dist/exports/react.d.ts +8 -3
- package/dist/exports/react.js +6 -2
- package/dist/exports/rsc.d.ts +3 -1
- package/dist/exports/rsc.js +4 -2
- package/dist/exports/types.d.ts +16 -8
- package/dist/fields/buildFieldBlocks.js +28 -13
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +7 -0
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/checkbox.js +8 -0
- package/dist/fields/builtin/checkbox.js.map +1 -1
- package/dist/fields/builtin/consent.js +27 -21
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/country.d.ts +13 -0
- package/dist/fields/builtin/country.js +30 -0
- package/dist/fields/builtin/country.js.map +1 -0
- package/dist/fields/builtin/date.js +2 -0
- package/dist/fields/builtin/date.js.map +1 -1
- package/dist/fields/builtin/file.js +5 -4
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +8 -6
- package/dist/fields/builtin/index.js +10 -9
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +8 -4
- package/dist/fields/builtin/message.js.map +1 -1
- package/dist/fields/builtin/repeater.js +1 -0
- package/dist/fields/builtin/repeater.js.map +1 -1
- package/dist/fields/builtin/select.js +1 -0
- package/dist/fields/builtin/select.js.map +1 -1
- package/dist/fields/builtin/state.d.ts +13 -0
- package/dist/fields/builtin/state.js +30 -0
- package/dist/fields/builtin/state.js.map +1 -0
- package/dist/fields/data/regions.d.ts +21 -0
- package/dist/fields/data/regions.js +1238 -0
- package/dist/fields/data/regions.js.map +1 -0
- package/dist/fields/fieldKey.d.ts +15 -0
- package/dist/fields/fieldKey.js +16 -0
- package/dist/fields/fieldKey.js.map +1 -0
- package/dist/fields/fieldNamesOfType.js +13 -8
- package/dist/fields/fieldNamesOfType.js.map +1 -1
- package/dist/fields/instanceOptions.js +20 -0
- package/dist/fields/instanceOptions.js.map +1 -0
- package/dist/fields/registry.d.ts +1 -0
- package/dist/fields/registry.js +21 -2
- package/dist/fields/registry.js.map +1 -1
- package/dist/fields/sharedConfig.js +46 -9
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +54 -3
- package/dist/flow/engine.d.ts +6 -4
- package/dist/flow/engine.js +11 -6
- package/dist/flow/engine.js.map +1 -1
- package/dist/flow/normalizeFlow.js +16 -7
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/flow/types.d.ts +12 -3
- package/dist/flow/types.js +11 -0
- package/dist/flow/types.js.map +1 -0
- package/dist/form/pollState.d.ts +4 -4
- package/dist/form/pollState.js.map +1 -1
- package/dist/form/toFormDocument.d.ts +37 -12
- package/dist/form/toFormDocument.js +43 -7
- package/dist/form/toFormDocument.js.map +1 -1
- package/dist/form/types.d.ts +31 -14
- package/dist/index.d.ts +143 -28
- package/dist/index.js +43 -14
- package/dist/index.js.map +1 -1
- package/dist/plugin/customState.js +26 -0
- package/dist/plugin/customState.js.map +1 -0
- package/dist/plugin/registerCollections.js +23 -13
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +2 -0
- package/dist/poll/applyPollOptions.js +3 -2
- package/dist/poll/applyPollOptions.js.map +1 -1
- package/dist/poll/buildPollOptionSourceFields.js +2 -5
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -1
- package/dist/poll/closeJob.d.ts +52 -0
- package/dist/poll/closeJob.js +119 -0
- package/dist/poll/closeJob.js.map +1 -0
- package/dist/poll/definePollOptionSource.d.ts +10 -1
- package/dist/poll/definePollOptionSource.js.map +1 -1
- package/dist/poll/definePollType.d.ts +34 -0
- package/dist/poll/definePollType.js +6 -0
- package/dist/poll/definePollType.js.map +1 -0
- package/dist/poll/effectivePollOptions.d.ts +38 -0
- package/dist/poll/effectivePollOptions.js +64 -0
- package/dist/poll/effectivePollOptions.js.map +1 -0
- package/dist/poll/mostVoted.d.ts +21 -0
- package/dist/poll/mostVoted.js +32 -0
- package/dist/poll/mostVoted.js.map +1 -0
- package/dist/poll/outcomeBeforeChange.js +91 -0
- package/dist/poll/outcomeBeforeChange.js.map +1 -0
- package/dist/poll/outcomeFields.d.ts +38 -0
- package/dist/poll/outcomeFields.js +52 -0
- package/dist/poll/outcomeFields.js.map +1 -0
- package/dist/poll/pollTypeRegistry.d.ts +34 -0
- package/dist/poll/pollTypeRegistry.js +71 -0
- package/dist/poll/pollTypeRegistry.js.map +1 -0
- package/dist/poll/resolvePollCloseRequest.js +105 -0
- package/dist/poll/resolvePollCloseRequest.js.map +1 -0
- package/dist/poll/resolvePollOptions.d.ts +2 -1
- package/dist/poll/resolvePollOptions.js +5 -17
- package/dist/poll/resolvePollOptions.js.map +1 -1
- package/dist/poll/resolvePollOptionsRequest.js +51 -0
- package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
- package/dist/poll/resolvePollOutcome.d.ts +22 -9
- package/dist/poll/resolvePollOutcome.js +54 -14
- package/dist/poll/resolvePollOutcome.js.map +1 -1
- package/dist/poll/resultsField.js +21 -0
- package/dist/poll/resultsField.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.js +2 -1
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/react/Form.d.ts +8 -8
- package/dist/react/Form.js +74 -62
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +6 -4
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormControls.d.ts +3 -3
- package/dist/react/FormControls.js +3 -3
- package/dist/react/FormControls.js.map +1 -1
- package/dist/react/FormResults.d.ts +5 -5
- package/dist/react/FormResults.js +6 -6
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/Poll.d.ts +2 -2
- package/dist/react/Poll.js +5 -5
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/contract.d.ts +2 -3
- package/dist/react/contract.js.map +1 -1
- package/dist/react/primitives/Checkbox.d.ts +4 -2
- package/dist/react/primitives/Checkbox.js +2 -1
- package/dist/react/primitives/Checkbox.js.map +1 -1
- package/dist/react/primitives/FieldShell.d.ts +2 -4
- package/dist/react/primitives/FieldShell.js +12 -19
- package/dist/react/primitives/FieldShell.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -2
- package/dist/react/renderers/calculation.js.map +1 -1
- package/dist/react/renderers/checkbox.js +1 -2
- package/dist/react/renderers/checkbox.js.map +1 -1
- package/dist/react/renderers/consent.js +78 -34
- package/dist/react/renderers/consent.js.map +1 -1
- package/dist/react/renderers/date.js +1 -3
- package/dist/react/renderers/date.js.map +1 -1
- package/dist/react/renderers/email.js +1 -2
- package/dist/react/renderers/email.js.map +1 -1
- package/dist/react/renderers/file.js +1 -2
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +3 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/number.js +1 -2
- package/dist/react/renderers/number.js.map +1 -1
- package/dist/react/renderers/region.js +45 -0
- package/dist/react/renderers/region.js.map +1 -0
- package/dist/react/renderers/repeater.js +7 -10
- package/dist/react/renderers/repeater.js.map +1 -1
- package/dist/react/renderers/select.js +1 -2
- package/dist/react/renderers/select.js.map +1 -1
- package/dist/react/renderers/text.js +1 -2
- package/dist/react/renderers/text.js.map +1 -1
- package/dist/react/renderers/textarea.js +1 -2
- package/dist/react/renderers/textarea.js.map +1 -1
- package/dist/react/state.d.ts +0 -3
- package/dist/react/state.js +7 -11
- package/dist/react/state.js.map +1 -1
- package/dist/react/useField.d.ts +0 -1
- package/dist/react/useField.js +0 -1
- package/dist/react/useField.js.map +1 -1
- package/dist/react/useFormState.d.ts +1 -1
- package/dist/react/useFormState.js +1 -1
- package/dist/react/useFormState.js.map +1 -1
- package/dist/react/validateField.js +3 -6
- package/dist/react/validateField.js.map +1 -1
- package/dist/recall/interpolate.d.ts +4 -3
- package/dist/recall/interpolate.js +5 -4
- package/dist/recall/interpolate.js.map +1 -1
- package/dist/recall/resolver.d.ts +1 -1
- package/dist/recall/resolver.js +14 -2
- package/dist/recall/resolver.js.map +1 -1
- package/dist/spam/captcha.d.ts +1 -1
- package/dist/spam/captcha.js +1 -1
- package/dist/spam/captcha.js.map +1 -1
- package/dist/submissions/SubmissionAnswers.js +6 -2
- package/dist/submissions/SubmissionAnswers.js.map +1 -1
- package/dist/submissions/SubmissionAnswersClient.js +91 -62
- package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
- package/dist/submissions/createSubmission.d.ts +36 -0
- package/dist/submissions/createSubmission.js +26 -0
- package/dist/submissions/createSubmission.js.map +1 -0
- package/dist/submissions/runSubmission.d.ts +10 -0
- package/dist/submissions/runSubmission.js +29 -18
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/types.d.ts +11 -4
- package/dist/submissions/validateSubmission.js +38 -23
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/votedCookie.js +3 -3
- package/dist/submissions/votedCookie.js.map +1 -1
- package/dist/translations/de.js +102 -26
- package/dist/translations/de.js.map +1 -1
- package/dist/translations/en.js +103 -27
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +97 -21
- package/dist/translations/keys.js +98 -22
- package/dist/translations/keys.js.map +1 -1
- package/dist/validation/buildRuleBlocks.js +18 -17
- package/dist/validation/buildRuleBlocks.js.map +1 -1
- package/dist/validation/builtin/email.js +1 -0
- package/dist/validation/builtin/email.js.map +1 -1
- package/dist/validation/builtin/matchesField.js +3 -7
- package/dist/validation/builtin/matchesField.js.map +1 -1
- package/dist/validation/builtin/max.js +1 -0
- package/dist/validation/builtin/max.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +1 -0
- package/dist/validation/builtin/maxDate.js.map +1 -1
- package/dist/validation/builtin/maxLength.js +1 -0
- package/dist/validation/builtin/maxLength.js.map +1 -1
- package/dist/validation/builtin/min.js +1 -0
- package/dist/validation/builtin/min.js.map +1 -1
- package/dist/validation/builtin/minDate.js +1 -0
- package/dist/validation/builtin/minDate.js.map +1 -1
- package/dist/validation/builtin/minLength.js +1 -0
- package/dist/validation/builtin/minLength.js.map +1 -1
- package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
- package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
- package/dist/validation/builtin/oneOf.js +1 -0
- package/dist/validation/builtin/oneOf.js.map +1 -1
- package/dist/validation/builtin/pattern.js +11 -2
- package/dist/validation/builtin/pattern.js.map +1 -1
- package/dist/validation/builtin/url.js +1 -0
- package/dist/validation/builtin/url.js.map +1 -1
- package/dist/validation/builtin/validateRegexParams.js +44 -0
- package/dist/validation/builtin/validateRegexParams.js.map +1 -0
- package/dist/validation/fieldTargetParam.d.ts +22 -0
- package/dist/validation/fieldTargetParam.js +38 -0
- package/dist/validation/fieldTargetParam.js.map +1 -0
- package/dist/validation/runValidation.js +8 -29
- package/dist/validation/runValidation.js.map +1 -1
- package/dist/validation/types.d.ts +5 -11
- package/package.json +6 -6
- package/dist/consent/buildConsentSourceConfig.js +0 -49
- package/dist/consent/buildConsentSourceConfig.js.map +0 -1
- package/dist/consent/builtin/index.d.ts +0 -17
- package/dist/consent/builtin/index.js +0 -21
- package/dist/consent/builtin/index.js.map +0 -1
- package/dist/consent/builtin/pageReference.js +0 -75
- package/dist/consent/builtin/pageReference.js.map +0 -1
- package/dist/consent/builtin/static.js +0 -48
- package/dist/consent/builtin/static.js.map +0 -1
- package/dist/consent/defineConsentSource.d.ts +0 -35
- package/dist/consent/defineConsentSource.js +0 -6
- package/dist/consent/defineConsentSource.js.map +0 -1
- package/dist/consent/registry.d.ts +0 -16
- package/dist/consent/registry.js +0 -16
- package/dist/consent/registry.js.map +0 -1
- package/dist/consent/resolveConsentLinks.d.ts +0 -19
- package/dist/consent/resolveConsentLinks.js +0 -26
- package/dist/consent/resolveConsentLinks.js.map +0 -1
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { FieldLabel, TextInput } from "@payloadcms/ui";
|
|
2
|
+
import { FieldLabel, Pill, TextInput } from "@payloadcms/ui";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/submissions/SubmissionAnswersClient.tsx
|
|
5
5
|
const noop = (_e) => {};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
/** Horizontal gutter between fields sharing a row; also trimmed from each fractional basis so widths wrap cleanly rather than overflow. */
|
|
7
|
+
const FIELD_GAP = "calc(var(--base, 20px) / 2)";
|
|
8
|
+
const BASIS = {
|
|
9
|
+
full: "100%",
|
|
10
|
+
half: "50%",
|
|
11
|
+
third: "33.333%",
|
|
12
|
+
twoThirds: "66.666%"
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Flex sizing for one answer field. Full fields (and file links / repeaters) claim a whole row;
|
|
16
|
+
* fractional ones sit side by side, their basis trimmed by the gutter and floored by `min-width`
|
|
17
|
+
* so a half or third never collapses on a narrow admin panel (it wraps to its own row instead).
|
|
18
|
+
*/
|
|
19
|
+
const widthStyle = (width) => {
|
|
20
|
+
const basis = BASIS[width ?? "full"];
|
|
21
|
+
return basis === "100%" ? { flex: "1 1 100%" } : {
|
|
22
|
+
flex: `0 1 calc(${basis} - ${FIELD_GAP})`,
|
|
23
|
+
minWidth: "min(100%, 12rem)"
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const answersGrid = {
|
|
27
|
+
display: "flex",
|
|
28
|
+
flexWrap: "wrap",
|
|
29
|
+
columnGap: FIELD_GAP
|
|
30
|
+
};
|
|
31
|
+
const fullWidth = { flex: "1 1 100%" };
|
|
15
32
|
const rowHeading = {
|
|
16
33
|
fontSize: "0.75rem",
|
|
17
34
|
fontWeight: 600,
|
|
18
35
|
color: "var(--theme-elevation-500)",
|
|
19
36
|
margin: "0 0 4px"
|
|
20
37
|
};
|
|
38
|
+
const linkStyle = {
|
|
39
|
+
display: "block",
|
|
40
|
+
padding: "8px 0",
|
|
41
|
+
color: "var(--theme-text)",
|
|
42
|
+
textDecoration: "underline"
|
|
43
|
+
};
|
|
21
44
|
const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels }) => {
|
|
22
45
|
if (!(answers.length > 0 || repeaters.length > 0 || consent.length > 0 || meta.length > 0)) return /* @__PURE__ */ jsx("p", {
|
|
23
46
|
style: { color: "var(--theme-elevation-500)" },
|
|
@@ -31,20 +54,17 @@ const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels })
|
|
|
31
54
|
gap: "32px"
|
|
32
55
|
},
|
|
33
56
|
children: [
|
|
34
|
-
(answers.length > 0 || repeaters.length > 0) && /* @__PURE__ */ jsxs("section", { children: [
|
|
35
|
-
|
|
36
|
-
|
|
57
|
+
(answers.length > 0 || repeaters.length > 0) && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.answers }), /* @__PURE__ */ jsxs("div", {
|
|
58
|
+
className: "render-fields",
|
|
59
|
+
style: answersGrid,
|
|
60
|
+
children: [answers.map(({ field, label, value, href, width }) => href ? /* @__PURE__ */ jsxs("div", {
|
|
37
61
|
className: "field-type",
|
|
62
|
+
style: fullWidth,
|
|
38
63
|
children: [/* @__PURE__ */ jsx(FieldLabel, { label }), /* @__PURE__ */ jsx("a", {
|
|
39
64
|
href,
|
|
40
65
|
target: "_blank",
|
|
41
66
|
rel: "noreferrer",
|
|
42
|
-
style:
|
|
43
|
-
display: "block",
|
|
44
|
-
padding: "8px 0",
|
|
45
|
-
color: "var(--theme-text)",
|
|
46
|
-
textDecoration: "underline"
|
|
47
|
-
},
|
|
67
|
+
style: linkStyle,
|
|
48
68
|
children: value
|
|
49
69
|
})]
|
|
50
70
|
}, field) : /* @__PURE__ */ jsx(TextInput, {
|
|
@@ -52,10 +72,11 @@ const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels })
|
|
|
52
72
|
label,
|
|
53
73
|
value,
|
|
54
74
|
readOnly: true,
|
|
55
|
-
onChange: noop
|
|
56
|
-
|
|
57
|
-
repeaters.map(({ field, label, rows }) => /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
onChange: noop,
|
|
76
|
+
style: widthStyle(width)
|
|
77
|
+
}, field)), repeaters.map(({ field, label, rows }) => /* @__PURE__ */ jsxs("div", {
|
|
58
78
|
className: "field-type",
|
|
79
|
+
style: fullWidth,
|
|
59
80
|
children: [/* @__PURE__ */ jsx(FieldLabel, { label }), rows.length === 0 ? /* @__PURE__ */ jsx(TextInput, {
|
|
60
81
|
path: `sa-${field}-empty`,
|
|
61
82
|
label: "",
|
|
@@ -87,52 +108,60 @@ const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels })
|
|
|
87
108
|
}, subField.label))]
|
|
88
109
|
}, row.id))
|
|
89
110
|
})]
|
|
90
|
-
}, field))
|
|
91
|
-
] }),
|
|
92
|
-
consent.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.consent }),
|
|
93
|
-
className: "
|
|
94
|
-
children:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
flexDirection: "column",
|
|
98
|
-
gap: "4px"
|
|
99
|
-
},
|
|
100
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
111
|
+
}, field))]
|
|
112
|
+
})] }),
|
|
113
|
+
consent.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.consent }), /* @__PURE__ */ jsx("div", {
|
|
114
|
+
className: "render-fields",
|
|
115
|
+
children: consent.map((entry) => /* @__PURE__ */ jsxs("div", {
|
|
116
|
+
className: "field-type",
|
|
117
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, { label: entry.field }), /* @__PURE__ */ jsxs("div", {
|
|
101
118
|
style: {
|
|
102
119
|
display: "flex",
|
|
103
|
-
|
|
104
|
-
gap: "
|
|
105
|
-
flexWrap: "wrap"
|
|
120
|
+
flexDirection: "column",
|
|
121
|
+
gap: "4px"
|
|
106
122
|
},
|
|
107
|
-
children: [/* @__PURE__ */
|
|
108
|
-
style: pillStyle(entry.agreed),
|
|
109
|
-
children: entry.agreed ? labels.agreed : labels.declined
|
|
110
|
-
}), entry.ref ? /* @__PURE__ */ jsxs("a", {
|
|
111
|
-
href: entry.ref,
|
|
112
|
-
target: "_blank",
|
|
113
|
-
rel: "noreferrer",
|
|
123
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
114
124
|
style: {
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
display: "flex",
|
|
126
|
+
alignItems: "center",
|
|
127
|
+
gap: "8px",
|
|
128
|
+
flexWrap: "wrap"
|
|
117
129
|
},
|
|
118
|
-
children: [
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
children: [/* @__PURE__ */ jsx(Pill, {
|
|
131
|
+
pillStyle: entry.agreed ? "success" : "error",
|
|
132
|
+
size: "small",
|
|
133
|
+
rounded: true,
|
|
134
|
+
children: entry.agreed ? labels.agreed : labels.declined
|
|
135
|
+
}), entry.ref ? /* @__PURE__ */ jsxs("a", {
|
|
136
|
+
href: entry.ref,
|
|
137
|
+
target: "_blank",
|
|
138
|
+
rel: "noreferrer",
|
|
139
|
+
style: {
|
|
140
|
+
fontSize: "0.8rem",
|
|
141
|
+
color: "var(--theme-text)"
|
|
142
|
+
},
|
|
143
|
+
children: [entry.ref, entry.versionRef ? ` (v${entry.versionRef})` : ""]
|
|
144
|
+
}) : null]
|
|
145
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
146
|
+
style: {
|
|
147
|
+
fontSize: "0.75rem",
|
|
148
|
+
color: "var(--theme-elevation-500)"
|
|
149
|
+
},
|
|
150
|
+
children: entry.at
|
|
151
|
+
})]
|
|
126
152
|
})]
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
meta.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.submissionDetails }),
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
}, entry.field))
|
|
154
|
+
})] }),
|
|
155
|
+
meta.length > 0 && /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx(FieldLabel, { label: labels.submissionDetails }), /* @__PURE__ */ jsx("div", {
|
|
156
|
+
className: "render-fields",
|
|
157
|
+
children: meta.map((item) => /* @__PURE__ */ jsx(TextInput, {
|
|
158
|
+
path: `sa-meta-${item.label}`,
|
|
159
|
+
label: item.label,
|
|
160
|
+
value: item.value,
|
|
161
|
+
readOnly: true,
|
|
162
|
+
onChange: noop
|
|
163
|
+
}, item.label))
|
|
164
|
+
})] })
|
|
136
165
|
]
|
|
137
166
|
});
|
|
138
167
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubmissionAnswersClient.js","names":[],"sources":["../../src/submissions/SubmissionAnswersClient.tsx"],"sourcesContent":["'use client'\n\nimport { FieldLabel, TextInput } from '@payloadcms/ui'\nimport type { ChangeEvent } from 'react'\n\nexport type AnswerItem = {\n\tfield: string\n\tlabel: string\n\tvalue: string\n\thref?: string\n\tmultiline?: boolean\n}\n\nexport type RepeaterRow = {\n\tid: string\n\tsubFields: Array<{ label: string; value: string }>\n}\n\nexport type RepeaterItem = {\n\tfield: string\n\tlabel: string\n\trows: RepeaterRow[]\n}\n\nexport type ConsentItem = {\n\tfield: string\n\tagreed: boolean\n\tref?: string\n\tversionRef?: string\n\tat: string\n}\n\nexport type MetaItem = {\n\tlabel: string\n\tvalue: string\n}\n\nexport type SubmissionAnswersLabels = {\n\tanswers: string\n\tconsent: string\n\tsubmissionDetails: string\n\tagreed: string\n\tdeclined: string\n\t/** Row heading template containing `{n}`, the 1-based row number. */\n\trow: string\n\tempty: string\n}\n\ntype Props = {\n\tanswers: AnswerItem[]\n\trepeaters: RepeaterItem[]\n\tconsent: ConsentItem[]\n\tmeta: MetaItem[]\n\tlabels: SubmissionAnswersLabels\n}\n\nconst noop = (_e: ChangeEvent<HTMLInputElement>) => {}\n\nconst pillStyle = (agreed: boolean): React.CSSProperties => ({\n\tdisplay: 'inline-block',\n\tpadding: '2px 8px',\n\tborderRadius: '9999px',\n\tfontSize: '0.75rem',\n\tfontWeight: 600,\n\tbackground: agreed ? 'var(--theme-success-500)' : 'var(--theme-error-500)',\n\tcolor: '#fff',\n})\n\nconst rowHeading: React.CSSProperties = {\n\tfontSize: '0.75rem',\n\tfontWeight: 600,\n\tcolor: 'var(--theme-elevation-500)',\n\tmargin: '0 0 4px',\n}\n\nexport const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels }: Props) => {\n\tconst hasContent =\n\t\tanswers.length > 0 || repeaters.length > 0 || consent.length > 0 || meta.length > 0\n\n\tif (!hasContent) {\n\t\treturn <p style={{ color: 'var(--theme-elevation-500)' }}>{labels.empty}</p>\n\t}\n\n\treturn (\n\t\t<div className=\"field-type\" style={{ display: 'flex', flexDirection: 'column', gap: '32px' }}>\n\t\t\t{(answers.length > 0 || repeaters.length > 0) && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.answers} />\n\t\t\t\t\t{answers.map(({ field, label, value, href }) =>\n\t\t\t\t\t\thref ? (\n\t\t\t\t\t\t\t<div key={field} className=\"field-type\">\n\t\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\thref={href}\n\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tdisplay: 'block',\n\t\t\t\t\t\t\t\t\t\tpadding: '8px 0',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--theme-text)',\n\t\t\t\t\t\t\t\t\t\ttextDecoration: 'underline',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\tkey={field}\n\t\t\t\t\t\t\t\tpath={`sa-${field}`}\n\t\t\t\t\t\t\t\tlabel={label}\n\t\t\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t)}\n\n\t\t\t\t\t{repeaters.map(({ field, label, rows }) => (\n\t\t\t\t\t\t<div key={field} className=\"field-type\">\n\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t{rows.length === 0 ? (\n\t\t\t\t\t\t\t\t<TextInput path={`sa-${field}-empty`} label=\"\" value=\"—\" readOnly onChange={noop} />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle={{ display: 'flex', flexDirection: 'column', gap: '8px', marginTop: '4px' }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{rows.map((row) => (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tkey={row.id}\n\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\tborder: '1px solid var(--theme-elevation-150)',\n\t\t\t\t\t\t\t\t\t\t\t\tborderRadius: '4px',\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: '4px 12px 0',\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<p style={rowHeading}>\n\t\t\t\t\t\t\t\t\t\t\t\t{labels.row.replace('{n}', String(Number(row.id) + 1))}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t{row.subFields.map((subField) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\tpath={`sa-${field}-${row.id}-${subField.label}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tlabel={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={subField.value}\n\t\t\t\t\t\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{consent.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.consent} />\n\t\t\t\t\t{consent.map((entry) => (\n\t\t\t\t\t\t<div key={entry.field} className=\"field-type\">\n\t\t\t\t\t\t\t<FieldLabel label={entry.field} />\n\t\t\t\t\t\t\t<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={pillStyle(entry.agreed)}>\n\t\t\t\t\t\t\t\t\t\t{entry.agreed ? labels.agreed : labels.declined}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{entry.ref ? (\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\thref={entry.ref}\n\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\tstyle={{ fontSize: '0.8rem', color: 'var(--theme-text)' }}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{entry.ref}\n\t\t\t\t\t\t\t\t\t\t\t{entry.versionRef ? ` (v${entry.versionRef})` : ''}\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<span style={{ fontSize: '0.75rem', color: 'var(--theme-elevation-500)' }}>\n\t\t\t\t\t\t\t\t\t{entry.at}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{meta.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.submissionDetails} />\n\t\t\t\t\t{meta.map((item) => (\n\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\tkey={item.label}\n\t\t\t\t\t\t\tpath={`sa-meta-${item.label}`}\n\t\t\t\t\t\t\tlabel={item.label}\n\t\t\t\t\t\t\tvalue={item.value}\n\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</section>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SubmissionAnswersClient.js","names":[],"sources":["../../src/submissions/SubmissionAnswersClient.tsx"],"sourcesContent":["'use client'\n\nimport { FieldLabel, Pill, TextInput } from '@payloadcms/ui'\nimport type { ChangeEvent } from 'react'\nimport type { SubmissionWidth } from './types'\n\nexport type AnswerItem = {\n\tfield: string\n\tlabel: string\n\tvalue: string\n\thref?: string\n\tmultiline?: boolean\n\twidth?: SubmissionWidth\n}\n\nexport type RepeaterRow = {\n\tid: string\n\tsubFields: Array<{ label: string; value: string }>\n}\n\nexport type RepeaterItem = {\n\tfield: string\n\tlabel: string\n\trows: RepeaterRow[]\n}\n\nexport type ConsentItem = {\n\tfield: string\n\tagreed: boolean\n\tref?: string\n\tversionRef?: string\n\tat: string\n}\n\nexport type MetaItem = {\n\tlabel: string\n\tvalue: string\n}\n\nexport type SubmissionAnswersLabels = {\n\tanswers: string\n\tconsent: string\n\tsubmissionDetails: string\n\tagreed: string\n\tdeclined: string\n\t/** Row heading template containing `{n}`, the 1-based row number. */\n\trow: string\n\tempty: string\n}\n\ntype Props = {\n\tanswers: AnswerItem[]\n\trepeaters: RepeaterItem[]\n\tconsent: ConsentItem[]\n\tmeta: MetaItem[]\n\tlabels: SubmissionAnswersLabels\n}\n\nconst noop = (_e: ChangeEvent<HTMLInputElement>) => {}\n\n/** Horizontal gutter between fields sharing a row; also trimmed from each fractional basis so widths wrap cleanly rather than overflow. */\nconst FIELD_GAP = 'calc(var(--base, 20px) / 2)'\n\nconst BASIS: Record<SubmissionWidth, string> = {\n\tfull: '100%',\n\thalf: '50%',\n\tthird: '33.333%',\n\ttwoThirds: '66.666%',\n}\n\n/**\n * Flex sizing for one answer field. Full fields (and file links / repeaters) claim a whole row;\n * fractional ones sit side by side, their basis trimmed by the gutter and floored by `min-width`\n * so a half or third never collapses on a narrow admin panel (it wraps to its own row instead).\n */\nconst widthStyle = (width?: SubmissionWidth): React.CSSProperties => {\n\tconst basis = BASIS[width ?? 'full']\n\treturn basis === '100%'\n\t\t? { flex: '1 1 100%' }\n\t\t: { flex: `0 1 calc(${basis} - ${FIELD_GAP})`, minWidth: 'min(100%, 12rem)' }\n}\n\nconst answersGrid: React.CSSProperties = {\n\tdisplay: 'flex',\n\tflexWrap: 'wrap',\n\tcolumnGap: FIELD_GAP,\n}\n\nconst fullWidth: React.CSSProperties = { flex: '1 1 100%' }\n\nconst rowHeading: React.CSSProperties = {\n\tfontSize: '0.75rem',\n\tfontWeight: 600,\n\tcolor: 'var(--theme-elevation-500)',\n\tmargin: '0 0 4px',\n}\n\nconst linkStyle: React.CSSProperties = {\n\tdisplay: 'block',\n\tpadding: '8px 0',\n\tcolor: 'var(--theme-text)',\n\ttextDecoration: 'underline',\n}\n\nexport const SubmissionAnswersClient = ({ answers, repeaters, consent, meta, labels }: Props) => {\n\tconst hasContent =\n\t\tanswers.length > 0 || repeaters.length > 0 || consent.length > 0 || meta.length > 0\n\n\tif (!hasContent) {\n\t\treturn <p style={{ color: 'var(--theme-elevation-500)' }}>{labels.empty}</p>\n\t}\n\n\treturn (\n\t\t<div className=\"field-type\" style={{ display: 'flex', flexDirection: 'column', gap: '32px' }}>\n\t\t\t{(answers.length > 0 || repeaters.length > 0) && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.answers} />\n\t\t\t\t\t<div className=\"render-fields\" style={answersGrid}>\n\t\t\t\t\t\t{answers.map(({ field, label, value, href, width }) =>\n\t\t\t\t\t\t\thref ? (\n\t\t\t\t\t\t\t\t<div key={field} className=\"field-type\" style={fullWidth}>\n\t\t\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t\t\t<a href={href} target=\"_blank\" rel=\"noreferrer\" style={linkStyle}>\n\t\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\t\tkey={field}\n\t\t\t\t\t\t\t\t\tpath={`sa-${field}`}\n\t\t\t\t\t\t\t\t\tlabel={label}\n\t\t\t\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t\t\tstyle={widthStyle(width)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{repeaters.map(({ field, label, rows }) => (\n\t\t\t\t\t\t\t<div key={field} className=\"field-type\" style={fullWidth}>\n\t\t\t\t\t\t\t\t<FieldLabel label={label} />\n\t\t\t\t\t\t\t\t{rows.length === 0 ? (\n\t\t\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\t\t\tpath={`sa-${field}-empty`}\n\t\t\t\t\t\t\t\t\t\tlabel=\"\"\n\t\t\t\t\t\t\t\t\t\tvalue=\"—\"\n\t\t\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\tdisplay: 'flex',\n\t\t\t\t\t\t\t\t\t\t\tflexDirection: 'column',\n\t\t\t\t\t\t\t\t\t\t\tgap: '8px',\n\t\t\t\t\t\t\t\t\t\t\tmarginTop: '4px',\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{rows.map((row) => (\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tkey={row.id}\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tborder: '1px solid var(--theme-elevation-150)',\n\t\t\t\t\t\t\t\t\t\t\t\t\tborderRadius: '4px',\n\t\t\t\t\t\t\t\t\t\t\t\t\tpadding: '4px 12px 0',\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<p style={rowHeading}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{labels.row.replace('{n}', String(Number(row.id) + 1))}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t{row.subFields.map((subField) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpath={`sa-${field}-${row.id}-${subField.label}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel={subField.label}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={subField.value}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{consent.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.consent} />\n\t\t\t\t\t<div className=\"render-fields\">\n\t\t\t\t\t\t{consent.map((entry) => (\n\t\t\t\t\t\t\t<div key={entry.field} className=\"field-type\">\n\t\t\t\t\t\t\t\t<FieldLabel label={entry.field} />\n\t\t\t\t\t\t\t\t<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Pill pillStyle={entry.agreed ? 'success' : 'error'} size=\"small\" rounded>\n\t\t\t\t\t\t\t\t\t\t\t{entry.agreed ? labels.agreed : labels.declined}\n\t\t\t\t\t\t\t\t\t\t</Pill>\n\t\t\t\t\t\t\t\t\t\t{entry.ref ? (\n\t\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\t\thref={entry.ref}\n\t\t\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={{ fontSize: '0.8rem', color: 'var(--theme-text)' }}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{entry.ref}\n\t\t\t\t\t\t\t\t\t\t\t\t{entry.versionRef ? ` (v${entry.versionRef})` : ''}\n\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<span style={{ fontSize: '0.75rem', color: 'var(--theme-elevation-500)' }}>\n\t\t\t\t\t\t\t\t\t\t{entry.at}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{meta.length > 0 && (\n\t\t\t\t<section>\n\t\t\t\t\t<FieldLabel label={labels.submissionDetails} />\n\t\t\t\t\t<div className=\"render-fields\">\n\t\t\t\t\t\t{meta.map((item) => (\n\t\t\t\t\t\t\t<TextInput\n\t\t\t\t\t\t\t\tkey={item.label}\n\t\t\t\t\t\t\t\tpath={`sa-meta-${item.label}`}\n\t\t\t\t\t\t\t\tlabel={item.label}\n\t\t\t\t\t\t\t\tvalue={item.value}\n\t\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t\t\tonChange={noop}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</section>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;AA0DA,MAAM,QAAQ,OAAsC,CAAC;;AAGrD,MAAM,YAAY;AAElB,MAAM,QAAyC;CAC9C,MAAM;CACN,MAAM;CACN,OAAO;CACP,WAAW;AACZ;;;;;;AAOA,MAAM,cAAc,UAAiD;CACpE,MAAM,QAAQ,MAAM,SAAS;CAC7B,OAAO,UAAU,SACd,EAAE,MAAM,WAAW,IACnB;EAAE,MAAM,YAAY,MAAM,KAAK,UAAU;EAAI,UAAU;CAAmB;AAC9E;AAEA,MAAM,cAAmC;CACxC,SAAS;CACT,UAAU;CACV,WAAW;AACZ;AAEA,MAAM,YAAiC,EAAE,MAAM,WAAW;AAE1D,MAAM,aAAkC;CACvC,UAAU;CACV,YAAY;CACZ,OAAO;CACP,QAAQ;AACT;AAEA,MAAM,YAAiC;CACtC,SAAS;CACT,SAAS;CACT,OAAO;CACP,gBAAgB;AACjB;AAEA,MAAa,2BAA2B,EAAE,SAAS,WAAW,SAAS,MAAM,aAAoB;CAIhG,IAAI,EAFH,QAAQ,SAAS,KAAK,UAAU,SAAS,KAAK,QAAQ,SAAS,KAAK,KAAK,SAAS,IAGlF,OAAO,oBAAC,KAAD;EAAG,OAAO,EAAE,OAAO,6BAA6B;YAAI,OAAO;CAAS,CAAA;CAG5E,OACC,qBAAC,OAAD;EAAK,WAAU;EAAa,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAO;YAA3F;IACG,QAAQ,SAAS,KAAK,UAAU,SAAS,MAC1C,qBAAC,WAAD,EAAA,UAAA,CACC,oBAAC,YAAD,EAAY,OAAO,OAAO,QAAU,CAAA,GACpC,qBAAC,OAAD;IAAK,WAAU;IAAgB,OAAO;cAAtC,CACE,QAAQ,KAAK,EAAE,OAAO,OAAO,OAAO,MAAM,YAC1C,OACC,qBAAC,OAAD;KAAiB,WAAU;KAAa,OAAO;eAA/C,CACC,oBAAC,YAAD,EAAmB,MAAQ,CAAA,GAC3B,oBAAC,KAAD;MAAS;MAAM,QAAO;MAAS,KAAI;MAAa,OAAO;gBACrD;KACC,CAAA,CACC;OALK,KAKL,IAEL,oBAAC,WAAD;KAEC,MAAM,MAAM;KACL;KACA;KACP,UAAA;KACA,UAAU;KACV,OAAO,WAAW,KAAK;IACvB,GAPK,KAOL,CAEH,GAEC,UAAU,KAAK,EAAE,OAAO,OAAO,WAC/B,qBAAC,OAAD;KAAiB,WAAU;KAAa,OAAO;eAA/C,CACC,oBAAC,YAAD,EAAmB,MAAQ,CAAA,GAC1B,KAAK,WAAW,IAChB,oBAAC,WAAD;MACC,MAAM,MAAM,MAAM;MAClB,OAAM;MACN,OAAM;MACN,UAAA;MACA,UAAU;KACV,CAAA,IAED,oBAAC,OAAD;MACC,OAAO;OACN,SAAS;OACT,eAAe;OACf,KAAK;OACL,WAAW;MACZ;gBAEC,KAAK,KAAK,QACV,qBAAC,OAAD;OAEC,OAAO;QACN,QAAQ;QACR,cAAc;QACd,SAAS;OACV;iBAND,CAQC,oBAAC,KAAD;QAAG,OAAO;kBACR,OAAO,IAAI,QAAQ,OAAO,OAAO,OAAO,IAAI,EAAE,IAAI,CAAC,CAAC;OACnD,CAAA,GACF,IAAI,UAAU,KAAK,aACnB,oBAAC,WAAD;QAEC,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,SAAS;QACxC,OAAO,SAAS;QAChB,OAAO,SAAS;QAChB,UAAA;QACA,UAAU;OACV,GANK,SAAS,KAMd,CACD,CACG;SApBC,IAAI,EAoBL,CACL;KACG,CAAA,CAEF;OA7CK,KA6CL,CACL,CACG;KACG,EAAA,CAAA;GAGT,QAAQ,SAAS,KACjB,qBAAC,WAAD,EAAA,UAAA,CACC,oBAAC,YAAD,EAAY,OAAO,OAAO,QAAU,CAAA,GACpC,oBAAC,OAAD;IAAK,WAAU;cACb,QAAQ,KAAK,UACb,qBAAC,OAAD;KAAuB,WAAU;eAAjC,CACC,oBAAC,YAAD,EAAY,OAAO,MAAM,MAAQ,CAAA,GACjC,qBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,eAAe;OAAU,KAAK;MAAM;gBAAnE,CACC,qBAAC,OAAD;OACC,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAO,UAAU;OAAO;iBAD9E,CAGC,oBAAC,MAAD;QAAM,WAAW,MAAM,SAAS,YAAY;QAAS,MAAK;QAAQ,SAAA;kBAChE,MAAM,SAAS,OAAO,SAAS,OAAO;OAClC,CAAA,GACL,MAAM,MACN,qBAAC,KAAD;QACC,MAAM,MAAM;QACZ,QAAO;QACP,KAAI;QACJ,OAAO;SAAE,UAAU;SAAU,OAAO;QAAoB;kBAJzD,CAME,MAAM,KACN,MAAM,aAAa,MAAM,MAAM,WAAW,KAAK,EAC9C;YACA,IACA;UACL,oBAAC,QAAD;OAAM,OAAO;QAAE,UAAU;QAAW,OAAO;OAA6B;iBACtE,MAAM;MACF,CAAA,CACF;OACD;OAzBK,MAAM,KAyBX,CACL;GACG,CAAA,CACG,EAAA,CAAA;GAGT,KAAK,SAAS,KACd,qBAAC,WAAD,EAAA,UAAA,CACC,oBAAC,YAAD,EAAY,OAAO,OAAO,kBAAoB,CAAA,GAC9C,oBAAC,OAAD;IAAK,WAAU;cACb,KAAK,KAAK,SACV,oBAAC,WAAD;KAEC,MAAM,WAAW,KAAK;KACtB,OAAO,KAAK;KACZ,OAAO,KAAK;KACZ,UAAA;KACA,UAAU;IACV,GANK,KAAK,KAMV,CACD;GACG,CAAA,CACG,EAAA,CAAA;EAEN;;AAEP"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SubmissionDescriptor, SubmissionValue } from "./types.js";
|
|
2
|
+
import { ConsentProofEntry } from "./runSubmission.js";
|
|
3
|
+
import { Payload, PayloadRequest } from "payload";
|
|
4
|
+
|
|
5
|
+
//#region src/submissions/createSubmission.d.ts
|
|
6
|
+
type CreateSubmissionArgs = {
|
|
7
|
+
form: number | string;
|
|
8
|
+
values: SubmissionValue[]; /** Thread the incoming request when there is one, so spam metadata (IP, user agent) and per-identity rate limiting see it. */
|
|
9
|
+
req?: PayloadRequest;
|
|
10
|
+
};
|
|
11
|
+
/** The stored shape of a `form-submissions` document, as written by the collection's own hooks. */
|
|
12
|
+
type CreatedSubmission = {
|
|
13
|
+
id: number | string;
|
|
14
|
+
form: number | string;
|
|
15
|
+
status: 'complete' | 'partial';
|
|
16
|
+
locale?: string;
|
|
17
|
+
values?: SubmissionValue[];
|
|
18
|
+
descriptors?: SubmissionDescriptor[];
|
|
19
|
+
consent?: ConsentProofEntry[];
|
|
20
|
+
meta?: Record<string, unknown>;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
updatedAt: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Server-side counterpart to the browser `submitForm` helper: a typed wrapper over `payload.create`
|
|
26
|
+
* for the `form-submissions` collection. Submitting this way runs the exact same pipeline a browser
|
|
27
|
+
* or raw REST submit does, server-authoritative validation, spam metadata capture when `req` is
|
|
28
|
+
* threaded through, descriptor snapshotting, consent capture, and post-submit action dispatch, since
|
|
29
|
+
* all of it lives in the collection's own hooks rather than in a route handler. Use it from server
|
|
30
|
+
* code (a script, a queued job, another collection's hook) instead of hand-rolling the collection
|
|
31
|
+
* slug and body shape.
|
|
32
|
+
*/
|
|
33
|
+
declare const createSubmission: (payload: Payload, args: CreateSubmissionArgs) => Promise<CreatedSubmission>;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { CreateSubmissionArgs, CreatedSubmission, createSubmission };
|
|
36
|
+
//# sourceMappingURL=createSubmission.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FORM_SUBMISSIONS_SLUG } from "../collections/formSubmissions.js";
|
|
2
|
+
//#region src/submissions/createSubmission.ts
|
|
3
|
+
/**
|
|
4
|
+
* Server-side counterpart to the browser `submitForm` helper: a typed wrapper over `payload.create`
|
|
5
|
+
* for the `form-submissions` collection. Submitting this way runs the exact same pipeline a browser
|
|
6
|
+
* or raw REST submit does, server-authoritative validation, spam metadata capture when `req` is
|
|
7
|
+
* threaded through, descriptor snapshotting, consent capture, and post-submit action dispatch, since
|
|
8
|
+
* all of it lives in the collection's own hooks rather than in a route handler. Use it from server
|
|
9
|
+
* code (a script, a queued job, another collection's hook) instead of hand-rolling the collection
|
|
10
|
+
* slug and body shape.
|
|
11
|
+
*/
|
|
12
|
+
const createSubmission = (payload, args) => {
|
|
13
|
+
const create = payload.create;
|
|
14
|
+
return create({
|
|
15
|
+
collection: FORM_SUBMISSIONS_SLUG,
|
|
16
|
+
data: {
|
|
17
|
+
form: args.form,
|
|
18
|
+
values: args.values
|
|
19
|
+
},
|
|
20
|
+
req: args.req
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createSubmission };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=createSubmission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSubmission.js","names":[],"sources":["../../src/submissions/createSubmission.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORM_SUBMISSIONS_SLUG } from '../collections/formSubmissions'\nimport type { ConsentProofEntry } from './runSubmission'\nimport type { SubmissionDescriptor, SubmissionValue } from './types'\n\nexport type CreateSubmissionArgs = {\n\tform: number | string\n\tvalues: SubmissionValue[]\n\t/** Thread the incoming request when there is one, so spam metadata (IP, user agent) and per-identity rate limiting see it. */\n\treq?: PayloadRequest\n}\n\n/** The stored shape of a `form-submissions` document, as written by the collection's own hooks. */\nexport type CreatedSubmission = {\n\tid: number | string\n\tform: number | string\n\tstatus: 'complete' | 'partial'\n\tlocale?: string\n\tvalues?: SubmissionValue[]\n\tdescriptors?: SubmissionDescriptor[]\n\tconsent?: ConsentProofEntry[]\n\tmeta?: Record<string, unknown>\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n/**\n * Server-side counterpart to the browser `submitForm` helper: a typed wrapper over `payload.create`\n * for the `form-submissions` collection. Submitting this way runs the exact same pipeline a browser\n * or raw REST submit does, server-authoritative validation, spam metadata capture when `req` is\n * threaded through, descriptor snapshotting, consent capture, and post-submit action dispatch, since\n * all of it lives in the collection's own hooks rather than in a route handler. Use it from server\n * code (a script, a queued job, another collection's hook) instead of hand-rolling the collection\n * slug and body shape.\n */\nexport const createSubmission = (\n\tpayload: Payload,\n\targs: CreateSubmissionArgs\n): Promise<CreatedSubmission> => {\n\t// `form-submissions` is registered by this plugin at runtime, so a host's generated Payload types\n\t// need not describe it, and when they do, pin its `form` relationship to whichever single id type\n\t// (string or number) that host's database uses. `form` here accepts either, so create through a\n\t// slug-agnostic signature instead of the host's own shape (same idiom as `registerReliability`'s\n\t// locks-collection create in the jobs plugin).\n\tconst create = payload.create as unknown as (options: {\n\t\tcollection: string\n\t\tdata: { form: number | string; values: SubmissionValue[] }\n\t\treq?: PayloadRequest\n\t}) => Promise<CreatedSubmission>\n\treturn create({\n\t\tcollection: FORM_SUBMISSIONS_SLUG,\n\t\tdata: { form: args.form, values: args.values },\n\t\treq: args.req,\n\t})\n}\n"],"mappings":";;;;;;;;;;;AAmCA,MAAa,oBACZ,SACA,SACgC;CAMhC,MAAM,SAAS,QAAQ;CAKvB,OAAO,OAAO;EACb,YAAY;EACZ,MAAM;GAAE,MAAM,KAAK;GAAM,QAAQ,KAAK;EAAO;EAC7C,KAAK,KAAK;CACX,CAAC;AACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConsentProof } from "../consent/captureConsent.js";
|
|
2
|
+
import { Payload, PayloadRequest } from "payload";
|
|
3
|
+
|
|
4
|
+
//#region src/submissions/runSubmission.d.ts
|
|
5
|
+
type ConsentProofEntry = {
|
|
6
|
+
field: string;
|
|
7
|
+
} & ConsentProof;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { ConsentProofEntry };
|
|
10
|
+
//# sourceMappingURL=runSubmission.d.ts.map
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { keys } from "../translations/keys.js";
|
|
2
|
+
import { isNamedField } from "../fields/fieldKey.js";
|
|
2
3
|
import { calcExpressionOf, computeCalcFields } from "../calc/computeCalcFields.js";
|
|
3
4
|
import { evaluateCondition } from "../conditions/evaluate.js";
|
|
4
5
|
import { captureConsent } from "../consent/captureConsent.js";
|
|
5
6
|
import { captureFileRef } from "../uploads/captureFileRef.js";
|
|
6
7
|
import { runValidation } from "../validation/runValidation.js";
|
|
7
8
|
//#region src/submissions/runSubmission.ts
|
|
9
|
+
const SUBMISSION_WIDTHS = new Set([
|
|
10
|
+
"full",
|
|
11
|
+
"half",
|
|
12
|
+
"third",
|
|
13
|
+
"twoThirds"
|
|
14
|
+
]);
|
|
15
|
+
/** The field instance's authored layout width, or undefined if unset/unrecognized (renders full). */
|
|
16
|
+
const widthOf = (instance) => typeof instance.width === "string" && SUBMISSION_WIDTHS.has(instance.width) ? instance.width : void 0;
|
|
8
17
|
const errorKeyFor = (code) => {
|
|
9
18
|
if (code === "mimeType") return keys.validationFileMimeType;
|
|
10
19
|
if (code === "tooLarge") return keys.validationFileTooLarge;
|
|
@@ -54,19 +63,18 @@ const optionLabelsFor = (instance) => {
|
|
|
54
63
|
* field whose `visibleWhen` is false is skipped entirely (never validated, never stored, so a client-sent
|
|
55
64
|
* value for it is ignored), and a visible field whose `validateWhen` is false stores its value but skips
|
|
56
65
|
* validation. A visible calc field stores its derived value and is never validated. Display-only field
|
|
57
|
-
* types (value kind 'none', e.g. message) are skipped in both passes: never
|
|
58
|
-
* a client-sent value under their name is dropped.
|
|
59
|
-
* but not surfaced server-side (the renderer surfaces them).
|
|
66
|
+
* types (value kind 'none', e.g. message) and nameless (bare) rows are skipped in both passes: never
|
|
67
|
+
* validated, never stored, and a client-sent value under their name is dropped.
|
|
60
68
|
*/
|
|
61
69
|
const runSubmission = async (input) => {
|
|
62
|
-
const { fields, values, registry, ruleRegistry,
|
|
70
|
+
const { fields, values, registry, ruleRegistry, consentEntries, locale, t, operation, req, payload, formId, uploadSlug, expectedOwner } = input;
|
|
63
71
|
const incoming = new Map(values.map((entry) => [entry.field, entry.value]));
|
|
64
72
|
const coercedAnswers = {};
|
|
65
73
|
const coercedByName = /* @__PURE__ */ new Map();
|
|
66
74
|
for (const instance of fields) {
|
|
67
75
|
const definition = registry.get(instance.blockType);
|
|
76
|
+
if (!definition || definition.value === "none" || calcExpressionOf(instance) || !isNamedField(instance)) continue;
|
|
68
77
|
const raw = incoming.get(instance.name);
|
|
69
|
-
if (!definition || definition.value === "none" || calcExpressionOf(instance)) continue;
|
|
70
78
|
const effectiveRaw = instance.blockType === "consent" && isEmpty(raw) ? false : instance.blockType === "repeater" && isEmpty(raw) ? [] : raw;
|
|
71
79
|
if (isEmpty(effectiveRaw) && instance.blockType !== "repeater") continue;
|
|
72
80
|
const value = coerce(definition.value, effectiveRaw);
|
|
@@ -81,7 +89,7 @@ const runSubmission = async (input) => {
|
|
|
81
89
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
82
90
|
for (const instance of fields) {
|
|
83
91
|
const definition = registry.get(instance.blockType);
|
|
84
|
-
if (!definition || definition.value === "none") continue;
|
|
92
|
+
if (!definition || definition.value === "none" || !isNamedField(instance)) continue;
|
|
85
93
|
const raw = incoming.get(instance.name);
|
|
86
94
|
const value = coercedByName.has(instance.name) ? coercedByName.get(instance.name) : raw;
|
|
87
95
|
if (!evaluateCondition(instance.visibleWhen, effective)) continue;
|
|
@@ -93,7 +101,8 @@ const runSubmission = async (input) => {
|
|
|
93
101
|
descriptors.push({
|
|
94
102
|
field: instance.name,
|
|
95
103
|
label: instance.label ?? instance.name,
|
|
96
|
-
fieldType: instance.blockType
|
|
104
|
+
fieldType: instance.blockType,
|
|
105
|
+
...widthOf(instance) ? { width: widthOf(instance) } : {}
|
|
97
106
|
});
|
|
98
107
|
continue;
|
|
99
108
|
}
|
|
@@ -114,11 +123,10 @@ const runSubmission = async (input) => {
|
|
|
114
123
|
payload,
|
|
115
124
|
formId
|
|
116
125
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (const issue of blocking) errors.push({
|
|
126
|
+
if (issues.length > 0) {
|
|
127
|
+
for (const message of issues) errors.push({
|
|
120
128
|
path: instance.name,
|
|
121
|
-
message
|
|
129
|
+
message
|
|
122
130
|
});
|
|
123
131
|
continue;
|
|
124
132
|
}
|
|
@@ -155,7 +163,8 @@ const runSubmission = async (input) => {
|
|
|
155
163
|
descriptors.push({
|
|
156
164
|
field: instance.name,
|
|
157
165
|
label: instance.label ?? instance.name,
|
|
158
|
-
fieldType: instance.blockType
|
|
166
|
+
fieldType: instance.blockType,
|
|
167
|
+
...widthOf(instance) ? { width: widthOf(instance) } : {}
|
|
159
168
|
});
|
|
160
169
|
continue;
|
|
161
170
|
}
|
|
@@ -166,7 +175,8 @@ const runSubmission = async (input) => {
|
|
|
166
175
|
descriptors.push({
|
|
167
176
|
field: instance.name,
|
|
168
177
|
label: instance.label ?? instance.name,
|
|
169
|
-
fieldType: instance.blockType
|
|
178
|
+
fieldType: instance.blockType,
|
|
179
|
+
...widthOf(instance) ? { width: widthOf(instance) } : {}
|
|
170
180
|
});
|
|
171
181
|
continue;
|
|
172
182
|
}
|
|
@@ -174,10 +184,9 @@ const runSubmission = async (input) => {
|
|
|
174
184
|
const proof = await captureConsent({
|
|
175
185
|
field: instance,
|
|
176
186
|
agreed: value === true,
|
|
177
|
-
|
|
187
|
+
entries: consentEntries ?? [],
|
|
178
188
|
payload,
|
|
179
189
|
req,
|
|
180
|
-
locale,
|
|
181
190
|
now
|
|
182
191
|
});
|
|
183
192
|
consentProofs.push({
|
|
@@ -188,7 +197,7 @@ const runSubmission = async (input) => {
|
|
|
188
197
|
}
|
|
189
198
|
if (instance.blockType === "repeater") {
|
|
190
199
|
const rows = Array.isArray(value) ? value : [];
|
|
191
|
-
const subFields = Array.isArray(instance.subFields) ? instance.subFields : [];
|
|
200
|
+
const subFields = (Array.isArray(instance.subFields) ? instance.subFields : []).filter(isNamedField);
|
|
192
201
|
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
193
202
|
const row = rows[rowIndex] ?? {};
|
|
194
203
|
for (const subField of subFields) {
|
|
@@ -213,9 +222,9 @@ const runSubmission = async (input) => {
|
|
|
213
222
|
payload,
|
|
214
223
|
formId
|
|
215
224
|
});
|
|
216
|
-
for (const
|
|
225
|
+
for (const message of subErrors) errors.push({
|
|
217
226
|
path: subPath,
|
|
218
|
-
message
|
|
227
|
+
message
|
|
219
228
|
});
|
|
220
229
|
}
|
|
221
230
|
if (subDef.value === "file" && payload) {
|
|
@@ -264,6 +273,7 @@ const runSubmission = async (input) => {
|
|
|
264
273
|
field: instance.name,
|
|
265
274
|
label: instance.label ?? instance.name,
|
|
266
275
|
fieldType: instance.blockType,
|
|
276
|
+
...widthOf(instance) ? { width: widthOf(instance) } : {},
|
|
267
277
|
...subFieldDescriptors.length > 0 ? { subFieldDescriptors } : {}
|
|
268
278
|
});
|
|
269
279
|
continue;
|
|
@@ -278,6 +288,7 @@ const runSubmission = async (input) => {
|
|
|
278
288
|
field: instance.name,
|
|
279
289
|
label: instance.label ?? instance.name,
|
|
280
290
|
fieldType: instance.blockType,
|
|
291
|
+
...widthOf(instance) ? { width: widthOf(instance) } : {},
|
|
281
292
|
...optionLabels ? { optionLabels } : {}
|
|
282
293
|
});
|
|
283
294
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runSubmission.js","names":[],"sources":["../../src/submissions/runSubmission.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport type { ConsentProof } from '../consent/captureConsent'\nimport { captureConsent } from '../consent/captureConsent'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { Translate } from '../fields/types'\nimport { keys } from '../translations/keys'\nimport { captureFileRef } from '../uploads/captureFileRef'\nimport type { FileFieldConfig, FileRefError } from '../uploads/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { runValidation } from '../validation/runValidation'\nimport type {\n\tFormFieldInstance,\n\tSubmissionDescriptor,\n\tSubmissionFieldError,\n\tSubmissionValue,\n} from './types'\n\nconst errorKeyFor = (code: FileRefError): string => {\n\tif (code === 'mimeType') {\n\t\treturn keys.validationFileMimeType\n\t}\n\tif (code === 'tooLarge') {\n\t\treturn keys.validationFileTooLarge\n\t}\n\treturn keys.validationFileMissing\n}\n\n/** Normalize the authored `mimeTypes` (a `hasMany` text field) to a `string[]`. */\nconst mimeTypesOf = (raw: unknown): string[] | undefined => {\n\tif (!Array.isArray(raw)) {\n\t\treturn undefined\n\t}\n\tconst out = (raw as unknown[]).filter((entry): entry is string => typeof entry === 'string')\n\treturn out.length > 0 ? out : undefined\n}\n\nconst fileFieldConfigOf = (instance: FormFieldInstance): FileFieldConfig => ({\n\tmimeTypes: mimeTypesOf(instance.mimeTypes),\n\tmaxSize: typeof instance.maxSize === 'number' ? instance.maxSize : undefined,\n})\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst coerce = (kind: string, value: unknown): unknown => {\n\tif (value == null) {\n\t\treturn value\n\t}\n\tif (kind === 'number') {\n\t\tconst next = typeof value === 'number' ? value : Number(value)\n\t\treturn Number.isNaN(next) ? value : next\n\t}\n\tif (kind === 'boolean') {\n\t\t// A genuine boolean from the renderer passes through; a raw client string is parsed strictly so that\n\t\t// \"false\"/\"0\"/\"off\"/\"no\"/\"\" are not silently truthy (this matters for the required-consent check).\n\t\tif (typeof value === 'string') {\n\t\t\tconst normalized = value.trim().toLowerCase()\n\t\t\treturn !(\n\t\t\t\tnormalized === '' ||\n\t\t\t\tnormalized === 'false' ||\n\t\t\t\tnormalized === '0' ||\n\t\t\t\tnormalized === 'off' ||\n\t\t\t\tnormalized === 'no'\n\t\t\t)\n\t\t}\n\t\treturn Boolean(value)\n\t}\n\tif (kind === 'text') {\n\t\treturn typeof value === 'string' ? value : String(value)\n\t}\n\treturn value\n}\n\nconst optionLabelsFor = (instance: FormFieldInstance): Record<string, string> | undefined => {\n\tconst options = instance.options\n\tif (!Array.isArray(options)) {\n\t\treturn undefined\n\t}\n\tconst map: Record<string, string> = {}\n\tfor (const option of options as Array<{ label?: string; value?: string }>) {\n\t\tif (typeof option?.value === 'string') {\n\t\t\tmap[option.value] = option.label ?? option.value\n\t\t}\n\t}\n\treturn Object.keys(map).length > 0 ? map : undefined\n}\n\nexport type ConsentProofEntry = { field: string } & ConsentProof\n\nexport type RunSubmissionInput = {\n\tfields: FormFieldInstance[]\n\tvalues: SubmissionValue[]\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/**\n\t * The plugin-configured uploads collection slug. The block's stamped `collection` is for the\n\t * client only; the server resolves the slug from plugin config and fails closed without one.\n\t */\n\tuploadSlug?: string\n\t/** Resolved request identity, verified against an upload's `owner` stamp when a file field is captured. */\n\texpectedOwner?: string\n}\n\nexport type RunSubmissionResult = {\n\terrors: SubmissionFieldError[]\n\tvalues: SubmissionValue[]\n\tdescriptors: SubmissionDescriptor[]\n\tconsent: ConsentProofEntry[]\n}\n\n/**\n * Pure submission core, two-pass: first coerce every answered field to its typed kind (so cross-field\n * rules see coerced siblings), then validate each field through `runValidation` (required, intrinsic\n * facet, declarative rules), snapshotting a localized descriptor per answered field. Calc fields are\n * the trust boundary: their client-sent values are never seeded, their values are derived from their\n * expressions over the coerced answers, and those derived values are authoritative everywhere downstream\n * (conditions, validation, storage). Conditions gate the second pass against these effective answers: a\n * field whose `visibleWhen` is false is skipped entirely (never validated, never stored, so a client-sent\n * value for it is ignored), and a visible field whose `validateWhen` is false stores its value but skips\n * validation. A visible calc field stores its derived value and is never validated. Display-only field\n * types (value kind 'none', e.g. message) are skipped in both passes: never validated, never stored, and\n * a client-sent value under their name is dropped. Only `error` severity blocks; warnings are computed\n * but not surfaced server-side (the renderer surfaces them).\n */\nexport const runSubmission = async (input: RunSubmissionInput): Promise<RunSubmissionResult> => {\n\tconst {\n\t\tfields,\n\t\tvalues,\n\t\tregistry,\n\t\truleRegistry,\n\t\tconsentRegistry,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\tuploadSlug,\n\t\texpectedOwner,\n\t} = input\n\tconst incoming = new Map(values.map((entry) => [entry.field, entry.value]))\n\n\tconst coercedAnswers: Record<string, unknown> = {}\n\tconst coercedByName = new Map<string, unknown>()\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\tconst raw = incoming.get(instance.name)\n\t\t// Never seed a calc field's client value: its value is derived below, so the client cannot influence it (even for a self-referencing expression).\n\t\t// A display-only ('none' kind) field carries no value at all, so a client-sent value under its name is dropped here.\n\t\tif (!definition || definition.value === 'none' || calcExpressionOf(instance)) {\n\t\t\tcontinue\n\t\t}\n\t\t// A consent field's \"not agreed\" state is semantically meaningful: treat a missing value as\n\t\t// `false` so the intrinsic validate can enforce required-agreement (not optional = must be true).\n\t\t// A repeater with no rows is coerced to [] so validate() can check minRows. The empty-guard\n\t\t// below must not skip repeaters: isEmpty([]) is true, but [] still needs to reach validate()\n\t\t// so a minRows > 0 constraint correctly rejects a zero-row submission.\n\t\tconst effectiveRaw =\n\t\t\tinstance.blockType === 'consent' && isEmpty(raw)\n\t\t\t\t? false\n\t\t\t\t: instance.blockType === 'repeater' && isEmpty(raw)\n\t\t\t\t\t? []\n\t\t\t\t\t: raw\n\t\tif (isEmpty(effectiveRaw) && instance.blockType !== 'repeater') {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = coerce(definition.value, effectiveRaw)\n\t\tcoercedAnswers[instance.name] = value\n\t\tcoercedByName.set(instance.name, value)\n\t}\n\n\t// Calc values are authoritative everywhere downstream (conditions, validation, storage), never the client-sent value.\n\tconst effective = computeCalcFields(fields, coercedAnswers)\n\n\tconst errors: SubmissionFieldError[] = []\n\tconst outValues: SubmissionValue[] = []\n\tconst descriptors: SubmissionDescriptor[] = []\n\tconst consentProofs: ConsentProofEntry[] = []\n\tconst now = new Date().toISOString()\n\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\t// A 'none'-kind (display-only) field is never validated and never stored: no value, no descriptor.\n\t\tif (!definition || definition.value === 'none') {\n\t\t\tcontinue\n\t\t}\n\t\tconst raw = incoming.get(instance.name)\n\t\tconst value = coercedByName.has(instance.name) ? coercedByName.get(instance.name) : raw\n\n\t\tif (!evaluateCondition(instance.visibleWhen, effective)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// A calc field's value is server-derived and always valid: skip validation and the client value entirely, storing the computed result.\n\t\tif (calcExpressionOf(instance)) {\n\t\t\toutValues.push({ field: instance.name, value: effective[instance.name] })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (evaluateCondition(instance.validateWhen, effective)) {\n\t\t\tconst { errors: issues } = await runValidation({\n\t\t\t\tfield: instance,\n\t\t\t\tfieldDefinition: definition,\n\t\t\t\tvalue,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers: effective,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t\toperation,\n\t\t\t\tevent: 'submit',\n\t\t\t\tmode: 'server',\n\t\t\t\treq,\n\t\t\t\tpayload,\n\t\t\t\tformId,\n\t\t\t})\n\t\t\tconst blocking = issues.filter((issue) => issue.severity === 'error')\n\t\t\tif (blocking.length > 0) {\n\t\t\t\tfor (const issue of blocking) {\n\t\t\t\t\terrors.push({ path: instance.name, message: issue.message })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// Gate on the field-type definition's value kind, not the blockType literal, so a custom\n\t\t// type registered with value:'file' gets the same server-side capture and enforcement as\n\t\t// the built-in file field (a blockType check would leave it storing the raw client id).\n\t\tif (definition.value === 'file') {\n\t\t\tif (isEmpty(value)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (payload) {\n\t\t\t\tif (!uploadSlug) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor('missing')) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst fileConfig = fileFieldConfigOf(instance)\n\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\tpayload,\n\t\t\t\t\tcollectionSlug: uploadSlug,\n\t\t\t\t\tuploadId: value as string | number,\n\t\t\t\t\tconfig: fileConfig,\n\t\t\t\t\treq,\n\t\t\t\t\texpectedOwner,\n\t\t\t\t})\n\t\t\t\tif (!captured.ok) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toutValues.push({ field: instance.name, value: captured.ref })\n\t\t\t\tdescriptors.push({\n\t\t\t\t\tfield: instance.name,\n\t\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutValues.push({ field: instance.name, value })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'consent' && payload) {\n\t\t\tconst proof = await captureConsent({\n\t\t\t\tfield: instance,\n\t\t\t\tagreed: value === true,\n\t\t\t\tregistry: consentRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tlocale,\n\t\t\t\tnow,\n\t\t\t})\n\t\t\tconsentProofs.push({ field: instance.name, ...proof })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'repeater') {\n\t\t\tconst rows = Array.isArray(value) ? (value as Array<Record<string, unknown>>) : []\n\t\t\tconst subFields = Array.isArray(instance.subFields)\n\t\t\t\t? (instance.subFields as FormFieldInstance[])\n\t\t\t\t: []\n\n\t\t\t// Per-row sub-field processing. Validation is gated by the sub-field's visibleWhen and\n\t\t\t// validateWhen against the row's own values; errors carry the path\n\t\t\t// fieldName[rowIndex].subFieldName so the client maps them to the right input. File\n\t\t\t// sub-fields additionally cross the server trust boundary: they are captured\n\t\t\t// unconditionally (a crafted request could carry an id for a conditionally-hidden field),\n\t\t\t// so a stored row never holds a raw, unenforced upload id.\n\t\t\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\t\t\tconst row = rows[rowIndex] ?? {}\n\t\t\t\tfor (const subField of subFields) {\n\t\t\t\t\tconst subDef = registry.get(subField.blockType)\n\t\t\t\t\tif (!subDef) continue\n\t\t\t\t\t// Display-only ('none' kind) sub-fields mirror the top-level skip: never validated,\n\t\t\t\t\t// never captured, and stripped from the stored row below so a client-injected value\n\t\t\t\t\t// under their name cannot ride along.\n\t\t\t\t\tif (subDef.value === 'none') continue\n\t\t\t\t\tconst subPath = `${instance.name}[${rowIndex}].${subField.name}`\n\t\t\t\t\tif (\n\t\t\t\t\t\tevaluateCondition(subField.visibleWhen, row) &&\n\t\t\t\t\t\tevaluateCondition(subField.validateWhen, row)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst { errors: subErrors } = await runValidation({\n\t\t\t\t\t\t\tfield: subField,\n\t\t\t\t\t\t\tfieldDefinition: subDef,\n\t\t\t\t\t\t\tvalue: row[subField.name],\n\t\t\t\t\t\t\tfieldType: subField.blockType,\n\t\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\t\tanswers: row,\n\t\t\t\t\t\t\tlocale,\n\t\t\t\t\t\t\tt,\n\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\tevent: 'submit',\n\t\t\t\t\t\t\tmode: 'server',\n\t\t\t\t\t\t\treq,\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\tformId,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tfor (const issue of subErrors.filter((e) => e.severity === 'error')) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message: issue.message })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Mirror the top-level file capture: the client submits only an upload id, so\n\t\t\t\t\t// re-derive filename/mimeType/filesize from the stored doc and enforce\n\t\t\t\t\t// owner/mime/size against the sub-field's config, failing closed on a missing\n\t\t\t\t\t// uploads slug or any capture rejection. The captured FileRef replaces the raw\n\t\t\t\t\t// id in the row. Without a payload (pure unit context) the value stays verbatim,\n\t\t\t\t\t// exactly like the top-level path.\n\t\t\t\t\tif (subDef.value === 'file' && payload) {\n\t\t\t\t\t\tconst subValue = row[subField.name]\n\t\t\t\t\t\tif (isEmpty(subValue)) {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!uploadSlug) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message: t(errorKeyFor('missing')) })\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\tcollectionSlug: uploadSlug,\n\t\t\t\t\t\t\tuploadId: subValue as string | number,\n\t\t\t\t\t\t\tconfig: fileFieldConfigOf(subField),\n\t\t\t\t\t\t\treq,\n\t\t\t\t\t\t\texpectedOwner,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif (!captured.ok) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow[subField.name] = captured.ref\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst subFieldDescriptors: SubmissionDescriptor[] = subFields.map((sf) => ({\n\t\t\t\tfield: sf.name,\n\t\t\t\tlabel: sf.label ?? sf.name,\n\t\t\t\tfieldType: sf.blockType,\n\t\t\t\t...((sf.options as unknown) ? { optionLabels: optionLabelsFor(sf) ?? undefined } : {}),\n\t\t\t}))\n\n\t\t\t// Strip display-only sub-fields from the stored rows: like top-level 'none' fields they\n\t\t\t// carry no answer, so a client-injected value under their name must never persist.\n\t\t\tconst displayOnly = new Set(\n\t\t\t\tsubFields.filter((sf) => registry.get(sf.blockType)?.value === 'none').map((sf) => sf.name)\n\t\t\t)\n\t\t\tconst storedRows =\n\t\t\t\tdisplayOnly.size === 0\n\t\t\t\t\t? rows\n\t\t\t\t\t: rows.map((row) =>\n\t\t\t\t\t\t\tObject.fromEntries(Object.entries(row).filter(([key]) => !displayOnly.has(key)))\n\t\t\t\t\t\t)\n\n\t\t\toutValues.push({ field: instance.name, value: storedRows })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t...(subFieldDescriptors.length > 0 ? { subFieldDescriptors } : {}),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isEmpty(raw)) {\n\t\t\tcontinue\n\t\t}\n\n\t\toutValues.push({ field: instance.name, value })\n\t\tconst optionLabels = optionLabelsFor(instance)\n\t\tdescriptors.push({\n\t\t\tfield: instance.name,\n\t\t\tlabel: instance.label ?? instance.name,\n\t\t\tfieldType: instance.blockType,\n\t\t\t...(optionLabels ? { optionLabels } : {}),\n\t\t})\n\t}\n\n\treturn { errors, values: outValues, descriptors, consent: consentProofs }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runSubmission.js","names":[],"sources":["../../src/submissions/runSubmission.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport type { ConsentProof } from '../consent/captureConsent'\nimport { captureConsent } from '../consent/captureConsent'\nimport type { ConsentSourceEntry } from '../consent/types'\nimport { isNamedField } from '../fields/fieldKey'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { Translate } from '../fields/types'\nimport { keys } from '../translations/keys'\nimport { captureFileRef } from '../uploads/captureFileRef'\nimport type { FileFieldConfig, FileRefError } from '../uploads/types'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { runValidation } from '../validation/runValidation'\nimport type {\n\tFormFieldInstance,\n\tSubmissionDescriptor,\n\tSubmissionFieldError,\n\tSubmissionValue,\n\tSubmissionWidth,\n} from './types'\n\nconst SUBMISSION_WIDTHS: ReadonlySet<string> = new Set<SubmissionWidth>([\n\t'full',\n\t'half',\n\t'third',\n\t'twoThirds',\n])\n\n/** The field instance's authored layout width, or undefined if unset/unrecognized (renders full). */\nexport const widthOf = (instance: FormFieldInstance): SubmissionWidth | undefined =>\n\ttypeof instance.width === 'string' && SUBMISSION_WIDTHS.has(instance.width)\n\t\t? (instance.width as SubmissionWidth)\n\t\t: undefined\n\nconst errorKeyFor = (code: FileRefError): string => {\n\tif (code === 'mimeType') {\n\t\treturn keys.validationFileMimeType\n\t}\n\tif (code === 'tooLarge') {\n\t\treturn keys.validationFileTooLarge\n\t}\n\treturn keys.validationFileMissing\n}\n\n/** Normalize the authored `mimeTypes` (a `hasMany` text field) to a `string[]`. */\nconst mimeTypesOf = (raw: unknown): string[] | undefined => {\n\tif (!Array.isArray(raw)) {\n\t\treturn undefined\n\t}\n\tconst out = (raw as unknown[]).filter((entry): entry is string => typeof entry === 'string')\n\treturn out.length > 0 ? out : undefined\n}\n\nconst fileFieldConfigOf = (instance: FormFieldInstance): FileFieldConfig => ({\n\tmimeTypes: mimeTypesOf(instance.mimeTypes),\n\tmaxSize: typeof instance.maxSize === 'number' ? instance.maxSize : undefined,\n})\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst coerce = (kind: string, value: unknown): unknown => {\n\tif (value == null) {\n\t\treturn value\n\t}\n\tif (kind === 'number') {\n\t\tconst next = typeof value === 'number' ? value : Number(value)\n\t\treturn Number.isNaN(next) ? value : next\n\t}\n\tif (kind === 'boolean') {\n\t\t// A genuine boolean from the renderer passes through; a raw client string is parsed strictly so that\n\t\t// \"false\"/\"0\"/\"off\"/\"no\"/\"\" are not silently truthy (this matters for the required-consent check).\n\t\tif (typeof value === 'string') {\n\t\t\tconst normalized = value.trim().toLowerCase()\n\t\t\treturn !(\n\t\t\t\tnormalized === '' ||\n\t\t\t\tnormalized === 'false' ||\n\t\t\t\tnormalized === '0' ||\n\t\t\t\tnormalized === 'off' ||\n\t\t\t\tnormalized === 'no'\n\t\t\t)\n\t\t}\n\t\treturn Boolean(value)\n\t}\n\tif (kind === 'text') {\n\t\treturn typeof value === 'string' ? value : String(value)\n\t}\n\treturn value\n}\n\nconst optionLabelsFor = (instance: FormFieldInstance): Record<string, string> | undefined => {\n\tconst options = instance.options\n\tif (!Array.isArray(options)) {\n\t\treturn undefined\n\t}\n\tconst map: Record<string, string> = {}\n\tfor (const option of options as Array<{ label?: string; value?: string }>) {\n\t\tif (typeof option?.value === 'string') {\n\t\t\tmap[option.value] = option.label ?? option.value\n\t\t}\n\t}\n\treturn Object.keys(map).length > 0 ? map : undefined\n}\n\nexport type ConsentProofEntry = { field: string } & ConsentProof\n\nexport type RunSubmissionInput = {\n\tfields: FormFieldInstance[]\n\tvalues: SubmissionValue[]\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\t/**\n\t * The host's consent sources, resolved once by the caller (which owns the request-scoped\n\t * resolver and how a failure surfaces) and read here to build each consent proof.\n\t */\n\tconsentEntries?: ConsentSourceEntry[]\n\tlocale: string\n\tt: Translate\n\toperation: 'create' | 'update'\n\treq?: PayloadRequest\n\tpayload?: Payload\n\tformId?: number | string\n\t/**\n\t * The plugin-configured uploads collection slug. The block's stamped `collection` is for the\n\t * client only; the server resolves the slug from plugin config and fails closed without one.\n\t */\n\tuploadSlug?: string\n\t/** Resolved request identity, verified against an upload's `owner` stamp when a file field is captured. */\n\texpectedOwner?: string\n}\n\nexport type RunSubmissionResult = {\n\terrors: SubmissionFieldError[]\n\tvalues: SubmissionValue[]\n\tdescriptors: SubmissionDescriptor[]\n\tconsent: ConsentProofEntry[]\n}\n\n/**\n * Pure submission core, two-pass: first coerce every answered field to its typed kind (so cross-field\n * rules see coerced siblings), then validate each field through `runValidation` (required, intrinsic\n * facet, declarative rules), snapshotting a localized descriptor per answered field. Calc fields are\n * the trust boundary: their client-sent values are never seeded, their values are derived from their\n * expressions over the coerced answers, and those derived values are authoritative everywhere downstream\n * (conditions, validation, storage). Conditions gate the second pass against these effective answers: a\n * field whose `visibleWhen` is false is skipped entirely (never validated, never stored, so a client-sent\n * value for it is ignored), and a visible field whose `validateWhen` is false stores its value but skips\n * validation. A visible calc field stores its derived value and is never validated. Display-only field\n * types (value kind 'none', e.g. message) and nameless (bare) rows are skipped in both passes: never\n * validated, never stored, and a client-sent value under their name is dropped.\n */\nexport const runSubmission = async (input: RunSubmissionInput): Promise<RunSubmissionResult> => {\n\tconst {\n\t\tfields,\n\t\tvalues,\n\t\tregistry,\n\t\truleRegistry,\n\t\tconsentEntries,\n\t\tlocale,\n\t\tt,\n\t\toperation,\n\t\treq,\n\t\tpayload,\n\t\tformId,\n\t\tuploadSlug,\n\t\texpectedOwner,\n\t} = input\n\tconst incoming = new Map(values.map((entry) => [entry.field, entry.value]))\n\n\tconst coercedAnswers: Record<string, unknown> = {}\n\tconst coercedByName = new Map<string, unknown>()\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\t// Never seed a calc field's client value: its value is derived below, so the client cannot influence it (even for a self-referencing expression).\n\t\t// A display-only ('none' kind) field carries no value at all, so a client-sent value under its name is dropped here.\n\t\t// A nameless (bare) row has no key to read a value under.\n\t\tif (\n\t\t\t!definition ||\n\t\t\tdefinition.value === 'none' ||\n\t\t\tcalcExpressionOf(instance) ||\n\t\t\t!isNamedField(instance)\n\t\t) {\n\t\t\tcontinue\n\t\t}\n\t\tconst raw = incoming.get(instance.name)\n\t\t// A consent field's \"not agreed\" state is semantically meaningful: treat a missing value as\n\t\t// `false` so the intrinsic validate can enforce required-agreement (not optional = must be true).\n\t\t// A repeater with no rows is coerced to [] so validate() can check minRows. The empty-guard\n\t\t// below must not skip repeaters: isEmpty([]) is true, but [] still needs to reach validate()\n\t\t// so a minRows > 0 constraint correctly rejects a zero-row submission.\n\t\tconst effectiveRaw =\n\t\t\tinstance.blockType === 'consent' && isEmpty(raw)\n\t\t\t\t? false\n\t\t\t\t: instance.blockType === 'repeater' && isEmpty(raw)\n\t\t\t\t\t? []\n\t\t\t\t\t: raw\n\t\tif (isEmpty(effectiveRaw) && instance.blockType !== 'repeater') {\n\t\t\tcontinue\n\t\t}\n\t\tconst value = coerce(definition.value, effectiveRaw)\n\t\tcoercedAnswers[instance.name] = value\n\t\tcoercedByName.set(instance.name, value)\n\t}\n\n\t// Calc values are authoritative everywhere downstream (conditions, validation, storage), never the client-sent value.\n\tconst effective = computeCalcFields(fields, coercedAnswers)\n\n\tconst errors: SubmissionFieldError[] = []\n\tconst outValues: SubmissionValue[] = []\n\tconst descriptors: SubmissionDescriptor[] = []\n\tconst consentProofs: ConsentProofEntry[] = []\n\tconst now = new Date().toISOString()\n\n\tfor (const instance of fields) {\n\t\tconst definition = registry.get(instance.blockType)\n\t\t// A 'none'-kind (display-only) field is never validated and never stored: no value, no descriptor.\n\t\t// A nameless (bare) row has no key to store under, so it is skipped the same way.\n\t\tif (!definition || definition.value === 'none' || !isNamedField(instance)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst raw = incoming.get(instance.name)\n\t\tconst value = coercedByName.has(instance.name) ? coercedByName.get(instance.name) : raw\n\n\t\tif (!evaluateCondition(instance.visibleWhen, effective)) {\n\t\t\tcontinue\n\t\t}\n\n\t\t// A calc field's value is server-derived and always valid: skip validation and the client value entirely, storing the computed result.\n\t\tif (calcExpressionOf(instance)) {\n\t\t\toutValues.push({ field: instance.name, value: effective[instance.name] })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t...(widthOf(instance) ? { width: widthOf(instance) } : {}),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (evaluateCondition(instance.validateWhen, effective)) {\n\t\t\tconst { errors: issues } = await runValidation({\n\t\t\t\tfield: instance,\n\t\t\t\tfieldDefinition: definition,\n\t\t\t\tvalue,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers: effective,\n\t\t\t\tlocale,\n\t\t\t\tt,\n\t\t\t\toperation,\n\t\t\t\tevent: 'submit',\n\t\t\t\tmode: 'server',\n\t\t\t\treq,\n\t\t\t\tpayload,\n\t\t\t\tformId,\n\t\t\t})\n\t\t\tif (issues.length > 0) {\n\t\t\t\tfor (const message of issues) {\n\t\t\t\t\terrors.push({ path: instance.name, message })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// Gate on the field-type definition's value kind, not the blockType literal, so a custom\n\t\t// type registered with value:'file' gets the same server-side capture and enforcement as\n\t\t// the built-in file field (a blockType check would leave it storing the raw client id).\n\t\tif (definition.value === 'file') {\n\t\t\tif (isEmpty(value)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (payload) {\n\t\t\t\tif (!uploadSlug) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor('missing')) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst fileConfig = fileFieldConfigOf(instance)\n\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\tpayload,\n\t\t\t\t\tcollectionSlug: uploadSlug,\n\t\t\t\t\tuploadId: value as string | number,\n\t\t\t\t\tconfig: fileConfig,\n\t\t\t\t\treq,\n\t\t\t\t\texpectedOwner,\n\t\t\t\t})\n\t\t\t\tif (!captured.ok) {\n\t\t\t\t\terrors.push({ path: instance.name, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toutValues.push({ field: instance.name, value: captured.ref })\n\t\t\t\tdescriptors.push({\n\t\t\t\t\tfield: instance.name,\n\t\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t\t...(widthOf(instance) ? { width: widthOf(instance) } : {}),\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutValues.push({ field: instance.name, value })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t...(widthOf(instance) ? { width: widthOf(instance) } : {}),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'consent' && payload) {\n\t\t\tconst proof = await captureConsent({\n\t\t\t\tfield: instance,\n\t\t\t\tagreed: value === true,\n\t\t\t\tentries: consentEntries ?? [],\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\tnow,\n\t\t\t})\n\t\t\tconsentProofs.push({ field: instance.name, ...proof })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (instance.blockType === 'repeater') {\n\t\t\tconst rows = Array.isArray(value) ? (value as Array<Record<string, unknown>>) : []\n\t\t\t// Nameless sub-rows are dropped like top-level ones (bare blocks are excluded from the\n\t\t\t// repeater's subFields config, so any encountered here is stray data).\n\t\t\tconst subFields = (\n\t\t\t\tArray.isArray(instance.subFields) ? (instance.subFields as FormFieldInstance[]) : []\n\t\t\t).filter(isNamedField)\n\n\t\t\t// Per-row sub-field processing. Validation is gated by the sub-field's visibleWhen and\n\t\t\t// validateWhen against the row's own values; errors carry the path\n\t\t\t// fieldName[rowIndex].subFieldName so the client maps them to the right input. File\n\t\t\t// sub-fields additionally cross the server trust boundary: they are captured\n\t\t\t// unconditionally (a crafted request could carry an id for a conditionally-hidden field),\n\t\t\t// so a stored row never holds a raw, unenforced upload id.\n\t\t\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\t\t\tconst row = rows[rowIndex] ?? {}\n\t\t\t\tfor (const subField of subFields) {\n\t\t\t\t\tconst subDef = registry.get(subField.blockType)\n\t\t\t\t\tif (!subDef) continue\n\t\t\t\t\t// Display-only ('none' kind) sub-fields mirror the top-level skip: never validated,\n\t\t\t\t\t// never captured, and stripped from the stored row below so a client-injected value\n\t\t\t\t\t// under their name cannot ride along.\n\t\t\t\t\tif (subDef.value === 'none') continue\n\t\t\t\t\tconst subPath = `${instance.name}[${rowIndex}].${subField.name}`\n\t\t\t\t\tif (\n\t\t\t\t\t\tevaluateCondition(subField.visibleWhen, row) &&\n\t\t\t\t\t\tevaluateCondition(subField.validateWhen, row)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst { errors: subErrors } = await runValidation({\n\t\t\t\t\t\t\tfield: subField,\n\t\t\t\t\t\t\tfieldDefinition: subDef,\n\t\t\t\t\t\t\tvalue: row[subField.name],\n\t\t\t\t\t\t\tfieldType: subField.blockType,\n\t\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\t\tanswers: row,\n\t\t\t\t\t\t\tlocale,\n\t\t\t\t\t\t\tt,\n\t\t\t\t\t\t\toperation,\n\t\t\t\t\t\t\tevent: 'submit',\n\t\t\t\t\t\t\tmode: 'server',\n\t\t\t\t\t\t\treq,\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\tformId,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tfor (const message of subErrors) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Mirror the top-level file capture: the client submits only an upload id, so\n\t\t\t\t\t// re-derive filename/mimeType/filesize from the stored doc and enforce\n\t\t\t\t\t// owner/mime/size against the sub-field's config, failing closed on a missing\n\t\t\t\t\t// uploads slug or any capture rejection. The captured FileRef replaces the raw\n\t\t\t\t\t// id in the row. Without a payload (pure unit context) the value stays verbatim,\n\t\t\t\t\t// exactly like the top-level path.\n\t\t\t\t\tif (subDef.value === 'file' && payload) {\n\t\t\t\t\t\tconst subValue = row[subField.name]\n\t\t\t\t\t\tif (isEmpty(subValue)) {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!uploadSlug) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message: t(errorKeyFor('missing')) })\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst captured = await captureFileRef({\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\tcollectionSlug: uploadSlug,\n\t\t\t\t\t\t\tuploadId: subValue as string | number,\n\t\t\t\t\t\t\tconfig: fileFieldConfigOf(subField),\n\t\t\t\t\t\t\treq,\n\t\t\t\t\t\t\texpectedOwner,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tif (!captured.ok) {\n\t\t\t\t\t\t\terrors.push({ path: subPath, message: t(errorKeyFor(captured.code)) })\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\trow[subField.name] = captured.ref\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst subFieldDescriptors: SubmissionDescriptor[] = subFields.map((sf) => ({\n\t\t\t\tfield: sf.name,\n\t\t\t\tlabel: sf.label ?? sf.name,\n\t\t\t\tfieldType: sf.blockType,\n\t\t\t\t...((sf.options as unknown) ? { optionLabels: optionLabelsFor(sf) ?? undefined } : {}),\n\t\t\t}))\n\n\t\t\t// Strip display-only sub-fields from the stored rows: like top-level 'none' fields they\n\t\t\t// carry no answer, so a client-injected value under their name must never persist.\n\t\t\tconst displayOnly = new Set(\n\t\t\t\tsubFields.filter((sf) => registry.get(sf.blockType)?.value === 'none').map((sf) => sf.name)\n\t\t\t)\n\t\t\tconst storedRows =\n\t\t\t\tdisplayOnly.size === 0\n\t\t\t\t\t? rows\n\t\t\t\t\t: rows.map((row) =>\n\t\t\t\t\t\t\tObject.fromEntries(Object.entries(row).filter(([key]) => !displayOnly.has(key)))\n\t\t\t\t\t\t)\n\n\t\t\toutValues.push({ field: instance.name, value: storedRows })\n\t\t\tdescriptors.push({\n\t\t\t\tfield: instance.name,\n\t\t\t\tlabel: instance.label ?? instance.name,\n\t\t\t\tfieldType: instance.blockType,\n\t\t\t\t...(widthOf(instance) ? { width: widthOf(instance) } : {}),\n\t\t\t\t...(subFieldDescriptors.length > 0 ? { subFieldDescriptors } : {}),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isEmpty(raw)) {\n\t\t\tcontinue\n\t\t}\n\n\t\toutValues.push({ field: instance.name, value })\n\t\tconst optionLabels = optionLabelsFor(instance)\n\t\tdescriptors.push({\n\t\t\tfield: instance.name,\n\t\t\tlabel: instance.label ?? instance.name,\n\t\t\tfieldType: instance.blockType,\n\t\t\t...(widthOf(instance) ? { width: widthOf(instance) } : {}),\n\t\t\t...(optionLabels ? { optionLabels } : {}),\n\t\t})\n\t}\n\n\treturn { errors, values: outValues, descriptors, consent: consentProofs }\n}\n"],"mappings":"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"}
|
|
@@ -6,12 +6,15 @@ type SubmissionValue = {
|
|
|
6
6
|
field: string;
|
|
7
7
|
value: unknown;
|
|
8
8
|
};
|
|
9
|
+
/** The four layout widths a field can occupy, shared by the renderer grid and the admin answers view. */
|
|
10
|
+
type SubmissionWidth = 'full' | 'half' | 'third' | 'twoThirds';
|
|
9
11
|
/** A localized, self-describing snapshot of an answered field, taken at submit time. */
|
|
10
12
|
type SubmissionDescriptor = {
|
|
11
13
|
field: string;
|
|
12
14
|
label: string;
|
|
13
15
|
fieldType: string;
|
|
14
|
-
optionLabels?: Record<string, string>; /**
|
|
16
|
+
optionLabels?: Record<string, string>; /** The field's authored layout width; absent on old submissions and width-less field types (renders full). */
|
|
17
|
+
width?: SubmissionWidth; /** For repeater fields: one descriptor per sub-field, shared across all rows. */
|
|
15
18
|
subFieldDescriptors?: SubmissionDescriptor[];
|
|
16
19
|
};
|
|
17
20
|
/** A per-field validation failure (`path` is the field name, for renderer error mapping). */
|
|
@@ -19,10 +22,14 @@ type SubmissionFieldError = {
|
|
|
19
22
|
path: string;
|
|
20
23
|
message: string;
|
|
21
24
|
};
|
|
22
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* A field instance as stored in a form's `fields` blocks array (shared config plus type-specific
|
|
27
|
+
* keys). `name` is absent on bare blocks (e.g. `message`), which are identified by the hidden
|
|
28
|
+
* block row `id` Payload injects into every row (see `fieldKey`).
|
|
29
|
+
*/
|
|
23
30
|
type FormFieldInstance = {
|
|
24
31
|
blockType: string;
|
|
25
|
-
name
|
|
32
|
+
name?: string;
|
|
26
33
|
label?: string;
|
|
27
34
|
required?: boolean;
|
|
28
35
|
visibleWhen?: Where;
|
|
@@ -30,5 +37,5 @@ type FormFieldInstance = {
|
|
|
30
37
|
[key: string]: unknown;
|
|
31
38
|
};
|
|
32
39
|
//#endregion
|
|
33
|
-
export { FormFieldInstance, SubmissionDescriptor, SubmissionFieldError, SubmissionValue };
|
|
40
|
+
export { FormFieldInstance, SubmissionDescriptor, SubmissionFieldError, SubmissionValue, SubmissionWidth };
|
|
34
41
|
//# sourceMappingURL=types.d.ts.map
|