@10x-media/form-builder 0.1.0-beta.2 → 0.1.0-beta.3
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 +18 -0
- package/README.md +2 -2
- package/dist/actions/body/converters.d.ts +20 -0
- package/dist/actions/body/converters.js +57 -0
- package/dist/actions/body/converters.js.map +1 -0
- package/dist/actions/body/escapeHtml.d.ts +6 -0
- package/dist/actions/body/escapeHtml.js +14 -0
- package/dist/actions/body/escapeHtml.js.map +1 -0
- package/dist/actions/body/serializeBody.d.ts +35 -0
- package/dist/actions/body/serializeBody.js +76 -0
- package/dist/actions/body/serializeBody.js.map +1 -0
- package/dist/actions/body/serializeSlate.js +31 -0
- package/dist/actions/body/serializeSlate.js.map +1 -0
- package/dist/actions/body/wildcards.d.ts +10 -0
- package/dist/actions/body/wildcards.js +33 -0
- package/dist/actions/body/wildcards.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +28 -12
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +7 -9
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/signedWebhook.js +21 -3
- package/dist/actions/builtin/signedWebhook.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -1
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/dispatch.js +2 -1
- package/dist/actions/dispatch.js.map +1 -1
- package/dist/actions/runActions.js +8 -1
- package/dist/actions/runActions.js.map +1 -1
- package/dist/actions/task.js +7 -5
- package/dist/actions/task.js.map +1 -1
- package/dist/calc/normalizeCalc.js +5 -17
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.d.ts +8 -2
- package/dist/calc/types.js +22 -0
- package/dist/calc/types.js.map +1 -0
- package/dist/client/ByteSizeField.d.ts +21 -0
- package/dist/client/ByteSizeField.js +56 -0
- package/dist/client/ByteSizeField.js.map +1 -0
- package/dist/client/ConditionBuilder.js +2 -1
- package/dist/client/ConditionBuilder.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +20 -0
- package/dist/client/FieldNameSelect.js +58 -0
- package/dist/client/FieldNameSelect.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +0 -1
- package/dist/client/FlowBuilder.js +4 -6
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/FormConditionField.js +1 -4
- package/dist/client/FormConditionField.js.map +1 -1
- package/dist/client/admin.css +192 -0
- package/dist/client/toStaticLabel.js +13 -0
- package/dist/client/toStaticLabel.js.map +1 -0
- package/dist/collections/formSubmissions.js +5 -3
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +74 -57
- package/dist/collections/forms.js.map +1 -1
- package/dist/exports/client.d.ts +3 -1
- package/dist/exports/client.js +3 -1
- package/dist/exports/react.d.ts +6 -1
- package/dist/exports/react.js +5 -1
- package/dist/fields/buildFieldBlocks.js +14 -18
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +115 -2
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/date.js +29 -0
- package/dist/fields/builtin/date.js.map +1 -0
- package/dist/fields/builtin/file.d.ts +15 -0
- package/dist/fields/builtin/file.js +79 -3
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +14 -0
- package/dist/fields/builtin/index.js +11 -2
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/fieldNamesOfType.js +25 -0
- package/dist/fields/fieldNamesOfType.js.map +1 -0
- package/dist/fields/sharedConfig.js +30 -18
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/index.d.ts +19 -2
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/plugin/registerCollections.js +3 -2
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/react/Form.js +3 -3
- package/dist/react/Form.js.map +1 -1
- package/dist/react/Poll.js +1 -1
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
- package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
- package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
- package/dist/react/captcha/TurnstileCaptcha.js +53 -0
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
- package/dist/react/captcha/types.d.ts +8 -0
- package/dist/react/captcha/useCaptchaScript.js +45 -0
- package/dist/react/captcha/useCaptchaScript.js.map +1 -0
- package/dist/react/captcha/vendors.js +8 -0
- package/dist/react/captcha/vendors.js.map +1 -0
- package/dist/react/primitives/Input.d.ts +1 -1
- package/dist/react/primitives/Input.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -1
- package/dist/react/renderers/checkbox.js +1 -1
- package/dist/react/renderers/consent.js +1 -1
- package/dist/react/renderers/date.js +37 -0
- package/dist/react/renderers/date.js.map +1 -0
- package/dist/react/renderers/email.js +1 -1
- package/dist/react/renderers/file.js +20 -7
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +2 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/number.js +1 -1
- package/dist/react/renderers/repeater.js +1 -1
- package/dist/react/renderers/select.js +1 -1
- package/dist/react/renderers/text.js +1 -1
- package/dist/react/renderers/textarea.js +1 -1
- package/dist/spam/captcha.d.ts +3 -2
- package/dist/spam/captcha.js +3 -2
- package/dist/spam/captcha.js.map +1 -1
- package/dist/spam/clientIp.js +9 -0
- package/dist/spam/clientIp.js.map +1 -0
- package/dist/spam/identify.js +3 -6
- package/dist/spam/identify.js.map +1 -1
- package/dist/spam/providers/hcaptcha.d.ts +17 -0
- package/dist/spam/providers/hcaptcha.js +18 -0
- package/dist/spam/providers/hcaptcha.js.map +1 -0
- package/dist/spam/providers/recaptcha.d.ts +23 -0
- package/dist/spam/providers/recaptcha.js +23 -0
- package/dist/spam/providers/recaptcha.js.map +1 -0
- package/dist/spam/providers/siteverify.js +34 -0
- package/dist/spam/providers/siteverify.js.map +1 -0
- package/dist/spam/providers/turnstile.d.ts +17 -0
- package/dist/spam/providers/turnstile.js +18 -0
- package/dist/spam/providers/turnstile.js.map +1 -0
- package/dist/spam/spamGuard.js +2 -5
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +2 -2
- package/dist/translations/en.js +26 -4
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +26 -4
- package/dist/translations/keys.js +26 -4
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/formatBytes.d.ts +9 -0
- package/dist/uploads/formatBytes.js +25 -0
- package/dist/uploads/formatBytes.js.map +1 -0
- package/dist/uploads/resolveFileRef.js +1 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/validation/builtin/index.d.ts +14 -0
- package/dist/validation/builtin/index.js +12 -1
- package/dist/validation/builtin/index.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +23 -0
- package/dist/validation/builtin/maxDate.js.map +1 -0
- package/dist/validation/builtin/minDate.js +23 -0
- package/dist/validation/builtin/minDate.js.map +1 -0
- package/dist/validation/builtin/validateDateBound.js +18 -0
- package/dist/validation/builtin/validateDateBound.js.map +1 -0
- package/package.json +3 -2
- package/styles/form-builder.css +0 -190
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlowBuilder.js","names":[],"sources":["../../src/client/FlowBuilder.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tButton,\n\tCheckboxInput,\n\tCollapsible,\n\tFieldLabel,\n\tReactSelect,\n\ttype ReactSelectOption,\n\tTextInput,\n\tuseField,\n\tuseFormFields,\n} from '@payloadcms/ui'\nimport { Popup, PopupList } from '@payloadcms/ui/elements/Popup'\nimport { MoreIcon } from '@payloadcms/ui/icons/More'\nimport { reduceFieldsToValues } from 'payload/shared'\nimport type { ChangeEvent } from 'react'\nimport { useMemo, useRef, useState } from 'react'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport type { FlowStep, FlowTransition, FormFlow } from '../flow/types'\nimport { ConditionBuilder } from './ConditionBuilder'\nimport type { FieldRow } from './synthesizeClientField'\n\nexport type FlowBuilderProps = {\n\tpath?: string\n\tfield?: { label?: unknown }\n\tlabel?: unknown\n\tconditionTypes: Record<string, ConditionFieldType>\n}\n\nconst toStaticLabel = (label: unknown): string | Record<string, string> | undefined => {\n\tif (typeof label === 'string') return label\n\tif (label && typeof label === 'object') return label as Record<string, string>\n\treturn undefined\n}\n\nconst extractFieldNames = (data: Record<string, unknown>): string[] => {\n\tconst rows = Array.isArray(data.fields) ? (data.fields as FieldRow[]) : []\n\treturn rows\n\t\t.map((row) => (typeof row.name === 'string' ? row.name.trim() : null))\n\t\t.filter((name): name is string => name !== null && name.length > 0)\n}\n\ntype TransitionRowProps = {\n\ttransition: FlowTransition\n\tstepIds: string[]\n\tconditionTypes: Record<string, ConditionFieldType>\n\tisFirst: boolean\n\tisLast: boolean\n\tonChange: (next: FlowTransition) => void\n\tonRemove: () => void\n\tonMoveUp: () => void\n\tonMoveDown: () => void\n}\n\nconst TransitionRow = ({\n\ttransition,\n\tstepIds,\n\tconditionTypes,\n\tisFirst,\n\tisLast,\n\tonChange,\n\tonRemove,\n\tonMoveUp,\n\tonMoveDown,\n}: TransitionRowProps) => {\n\tconst toOptions: ReactSelectOption[] = stepIds.map((id) => ({ label: id, value: id }))\n\n\tconst handleGotoChange = (selected: ReactSelectOption | ReactSelectOption[]) => {\n\t\tconst chosen = Array.isArray(selected) ? selected[0] : selected\n\t\tif (chosen) onChange({ ...transition, to: chosen.value as string })\n\t}\n\n\treturn (\n\t\t<div className=\"fb-flow-transition\">\n\t\t\t<div className=\"fb-flow-transition__header\">\n\t\t\t\t<span className=\"fb-flow-transition__label\">go to</span>\n\t\t\t\t<div className=\"fb-flow-transition__goto\">\n\t\t\t\t\t<ReactSelect\n\t\t\t\t\t\toptions={toOptions}\n\t\t\t\t\t\tvalue={toOptions.find((o) => o.value === transition.to) ?? undefined}\n\t\t\t\t\t\tplaceholder=\"Select step…\"\n\t\t\t\t\t\tisClearable={false}\n\t\t\t\t\t\tonChange={handleGotoChange}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"fb-flow-transition__actions\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"fb-flow__move-btn\"\n\t\t\t\t\t\tonClick={onMoveUp}\n\t\t\t\t\t\tdisabled={isFirst}\n\t\t\t\t\t\taria-label=\"Move transition up\"\n\t\t\t\t\t>\n\t\t\t\t\t\t↑\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"fb-flow__move-btn\"\n\t\t\t\t\t\tonClick={onMoveDown}\n\t\t\t\t\t\tdisabled={isLast}\n\t\t\t\t\t\taria-label=\"Move transition down\"\n\t\t\t\t\t>\n\t\t\t\t\t\t↓\n\t\t\t\t\t</button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\ticon=\"x\"\n\t\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t\taria-label=\"Remove transition\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<span className=\"fb-flow-transition__when-label\">when</span>\n\t\t\t<div className=\"fb-flow-transition__condition\">\n\t\t\t\t<ConditionBuilder\n\t\t\t\t\tvalue={transition.when}\n\t\t\t\t\tonChange={(when) => onChange({ ...transition, when })}\n\t\t\t\t\tconditionTypes={conditionTypes}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\ntype StepCardProps = {\n\tstep: FlowStep\n\tstepKey: string\n\tstepIndex: number\n\tstepCount: number\n\totherStepIds: string[]\n\tfieldNames: string[]\n\tconditionTypes: Record<string, ConditionFieldType>\n\tisExpanded: boolean\n\tonToggle: () => void\n\tonChange: (next: FlowStep) => void\n\tonRemove: () => void\n\tonMoveUp: () => void\n\tonMoveDown: () => void\n\tonAddAbove: () => void\n\tonAddBelow: () => void\n}\n\nconst StepCard = ({\n\tstep,\n\tstepKey,\n\tstepIndex,\n\tstepCount,\n\totherStepIds,\n\tfieldNames,\n\tconditionTypes,\n\tisExpanded,\n\tonToggle,\n\tonChange,\n\tonRemove,\n\tonMoveUp,\n\tonMoveDown,\n\tonAddAbove,\n\tonAddBelow,\n}: StepCardProps) => {\n\tconst nextOptions: ReactSelectOption[] = [\n\t\t{ label: '— terminal —', value: '' },\n\t\t...otherStepIds.map((id) => ({ label: id, value: id })),\n\t]\n\n\tconst transitions = step.transitions ?? []\n\n\t// Stable per-transition keys, mirroring the step-level scheme so transition reorder/remove\n\t// reconciles by identity rather than array position.\n\tconst tKeysRef = useRef<string[]>([])\n\tif (tKeysRef.current.length !== transitions.length) {\n\t\ttKeysRef.current = transitions.map((_, i) => tKeysRef.current[i] ?? crypto.randomUUID())\n\t}\n\tconst tKeys = tKeysRef.current\n\n\tconst addTransition = () => {\n\t\tconst firstOtherStep = otherStepIds[0]\n\t\tif (!firstOtherStep) return\n\t\ttKeysRef.current = [...tKeys, crypto.randomUUID()]\n\t\tonChange({\n\t\t\t...step,\n\t\t\ttransitions: [...transitions, { when: {}, to: firstOtherStep }],\n\t\t})\n\t}\n\n\tconst updateTransition = (index: number, next: FlowTransition) => {\n\t\tconst updated = [...transitions]\n\t\tupdated[index] = next\n\t\tonChange({ ...step, transitions: updated })\n\t}\n\n\tconst removeTransition = (index: number) => {\n\t\ttKeysRef.current = tKeys.filter((_, i) => i !== index)\n\t\tconst updated = transitions.filter((_, i) => i !== index)\n\t\tonChange({ ...step, transitions: updated.length > 0 ? updated : undefined })\n\t}\n\n\tconst moveTransition = (from: number, to: number) => {\n\t\tconst updated = [...transitions]\n\t\tconst [item] = updated.splice(from, 1)\n\t\tif (item !== undefined) updated.splice(to, 0, item)\n\t\tconst nextKeys = [...tKeys]\n\t\tconst [movedKey] = nextKeys.splice(from, 1)\n\t\tif (movedKey !== undefined) nextKeys.splice(to, 0, movedKey)\n\t\ttKeysRef.current = nextKeys\n\t\tonChange({ ...step, transitions: updated })\n\t}\n\n\tconst handleNextChange = (selected: ReactSelectOption | ReactSelectOption[]) => {\n\t\tconst chosen = Array.isArray(selected) ? selected[0] : selected\n\t\tconst next = (chosen?.value as string | undefined) || undefined\n\t\tonChange({ ...step, next })\n\t}\n\n\tconst stepHeader = (\n\t\t<div className=\"fb-flow-step__header-content\">\n\t\t\t<span className=\"fb-flow-step__index\">Step {stepIndex + 1}</span>\n\t\t\t<span className=\"fb-flow-step__id-preview\">\n\t\t\t\t{step.id.length > 0 ? step.id : <em>no id</em>}\n\t\t\t</span>\n\t\t</div>\n\t)\n\n\treturn (\n\t\t<Collapsible\n\t\t\theader={stepHeader}\n\t\t\tactions={\n\t\t\t\t<Popup\n\t\t\t\t\tbutton={<MoreIcon />}\n\t\t\t\t\tbuttonType=\"default\"\n\t\t\t\t\thorizontalAlign=\"right\"\n\t\t\t\t\tsize=\"fit-content\"\n\t\t\t\t\tverticalAlign=\"bottom\"\n\t\t\t\t>\n\t\t\t\t\t<PopupList.ButtonGroup>\n\t\t\t\t\t\t<PopupList.Button onClick={onAddAbove}>Add above</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onAddBelow}>Add below</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onMoveUp} disabled={stepIndex === 0}>\n\t\t\t\t\t\t\tMove up\n\t\t\t\t\t\t</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onMoveDown} disabled={stepIndex === stepCount - 1}>\n\t\t\t\t\t\t\tMove down\n\t\t\t\t\t\t</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onRemove}>Remove</PopupList.Button>\n\t\t\t\t\t</PopupList.ButtonGroup>\n\t\t\t\t</Popup>\n\t\t\t}\n\t\t\tisCollapsed={!isExpanded}\n\t\t\tonToggle={() => onToggle()}\n\t\t\tclassName=\"fb-flow-step\"\n\t\t>\n\t\t\t<div className=\"fb-flow-step__body\">\n\t\t\t\t<TextInput\n\t\t\t\t\tpath={`fb-flow-step-${stepKey}-id`}\n\t\t\t\t\tlabel=\"Step ID\"\n\t\t\t\t\tvalue={step.id}\n\t\t\t\t\tplaceholder=\"e.g. intro\"\n\t\t\t\t\tonChange={(e: ChangeEvent<HTMLInputElement>) => onChange({ ...step, id: e.target.value })}\n\t\t\t\t/>\n\n\t\t\t\t<TextInput\n\t\t\t\t\tpath={`fb-flow-step-${stepKey}-title`}\n\t\t\t\t\tlabel=\"Title\"\n\t\t\t\t\tvalue={step.title ?? ''}\n\t\t\t\t\tplaceholder=\"Optional display title\"\n\t\t\t\t\tonChange={(e: ChangeEvent<HTMLInputElement>) =>\n\t\t\t\t\t\tonChange({ ...step, title: e.target.value.length > 0 ? e.target.value : undefined })\n\t\t\t\t\t}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"fb-flow-step__row\">\n\t\t\t\t\t<FieldLabel label=\"Fields\" />\n\t\t\t\t\t{fieldNames.length === 0 ? (\n\t\t\t\t\t\t<p className=\"fb-flow-step__hint\">No fields defined on the form yet.</p>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"fb-flow-step__field-picker\">\n\t\t\t\t\t\t\t{fieldNames.map((name) => (\n\t\t\t\t\t\t\t\t<CheckboxInput\n\t\t\t\t\t\t\t\t\tkey={name}\n\t\t\t\t\t\t\t\t\tlabel={name}\n\t\t\t\t\t\t\t\t\tchecked={step.fields.includes(name)}\n\t\t\t\t\t\t\t\t\tonToggle={(e) => {\n\t\t\t\t\t\t\t\t\t\tconst fields = e.target.checked\n\t\t\t\t\t\t\t\t\t\t\t? [...step.fields, name]\n\t\t\t\t\t\t\t\t\t\t\t: step.fields.filter((f) => f !== name)\n\t\t\t\t\t\t\t\t\t\tonChange({ ...step, fields })\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))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"fb-flow-step__row\">\n\t\t\t\t\t<FieldLabel label=\"Default next\" />\n\t\t\t\t\t<ReactSelect\n\t\t\t\t\t\toptions={nextOptions}\n\t\t\t\t\t\tvalue={\n\t\t\t\t\t\t\tstep.next\n\t\t\t\t\t\t\t\t? (nextOptions.find((o) => o.value === step.next) ?? undefined)\n\t\t\t\t\t\t\t\t: (nextOptions[0] ?? undefined)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisClearable={false}\n\t\t\t\t\t\tonChange={handleNextChange}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"fb-flow-step__transitions\">\n\t\t\t\t\t<div className=\"fb-flow-step__transitions-header\">\n\t\t\t\t\t\t<FieldLabel label=\"Conditional transitions\" />\n\t\t\t\t\t\t<span className=\"fb-flow-step__hint\">(first match wins)</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{transitions.map((transition, tIndex) => {\n\t\t\t\t\t\tconst tKey = tKeys[tIndex]\n\t\t\t\t\t\tif (tKey === undefined) return null\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<TransitionRow\n\t\t\t\t\t\t\t\tkey={tKey}\n\t\t\t\t\t\t\t\ttransition={transition}\n\t\t\t\t\t\t\t\tstepIds={otherStepIds}\n\t\t\t\t\t\t\t\tconditionTypes={conditionTypes}\n\t\t\t\t\t\t\t\tisFirst={tIndex === 0}\n\t\t\t\t\t\t\t\tisLast={tIndex === transitions.length - 1}\n\t\t\t\t\t\t\t\tonChange={(next) => updateTransition(tIndex, next)}\n\t\t\t\t\t\t\t\tonRemove={() => removeTransition(tIndex)}\n\t\t\t\t\t\t\t\tonMoveUp={() => moveTransition(tIndex, tIndex - 1)}\n\t\t\t\t\t\t\t\tonMoveDown={() => moveTransition(tIndex, tIndex + 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t<Button\n\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\t\tonClick={addTransition}\n\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t>\n\t\t\t\t\t\tAdd transition\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\nexport const FlowBuilder = (props: FlowBuilderProps) => {\n\tconst { setValue, value } = useField<FormFlow | undefined>()\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\tconst [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set())\n\n\tconst fieldNamesJson = useFormFields(([fields]) =>\n\t\tJSON.stringify(extractFieldNames(reduceFieldsToValues(fields, true) as Record<string, unknown>))\n\t)\n\tconst fieldNames = useMemo(() => JSON.parse(fieldNamesJson) as string[], [fieldNamesJson])\n\n\tconst steps = value?.steps ?? []\n\tconst allStepIds = steps.map((s) => s.id).filter((id) => id.length > 0)\n\n\t// Stable per-step keys, decoupled from the user-editable step.id. Reorder/insert/remove\n\t// reconcile by identity so the focused Step ID input and each step's expand state stay with\n\t// their step instead of jumping to whichever step now sits at that array position.\n\tconst keysRef = useRef<string[]>([])\n\tif (keysRef.current.length !== steps.length) {\n\t\tkeysRef.current = steps.map((_, i) => keysRef.current[i] ?? crypto.randomUUID())\n\t}\n\tconst keys = keysRef.current\n\n\tconst toggleExpanded = (key: string) => {\n\t\tsetExpandedKeys((prev) => {\n\t\t\tconst next = new Set(prev)\n\t\t\tif (next.has(key)) next.delete(key)\n\t\t\telse next.add(key)\n\t\t\treturn next\n\t\t})\n\t}\n\n\tconst emit = (nextSteps: FlowStep[], nextKeys: string[]) => {\n\t\tkeysRef.current = nextKeys\n\t\tsetValue({ steps: nextSteps } as FormFlow)\n\t}\n\n\tconst insertStep = (at: number) => {\n\t\tconst key = crypto.randomUUID()\n\t\tconst nextSteps = [...steps]\n\t\tnextSteps.splice(at, 0, { id: crypto.randomUUID(), fields: [] })\n\t\tconst nextKeys = [...keys]\n\t\tnextKeys.splice(at, 0, key)\n\t\temit(nextSteps, nextKeys)\n\t\tsetExpandedKeys((prev) => new Set(prev).add(key))\n\t}\n\n\tconst addStep = () => insertStep(steps.length)\n\n\tconst updateStep = (index: number, next: FlowStep) => {\n\t\temit(\n\t\t\tsteps.map((s, i) => (i === index ? next : s)),\n\t\t\tkeys\n\t\t)\n\t}\n\n\tconst removeStep = (index: number) => {\n\t\tconst removedKey = keys[index]\n\t\temit(\n\t\t\tsteps.filter((_, i) => i !== index),\n\t\t\tkeys.filter((_, i) => i !== index)\n\t\t)\n\t\tif (removedKey !== undefined) {\n\t\t\tsetExpandedKeys((prev) => {\n\t\t\t\tconst next = new Set(prev)\n\t\t\t\tnext.delete(removedKey)\n\t\t\t\treturn next\n\t\t\t})\n\t\t}\n\t}\n\n\tconst moveStep = (from: number, to: number) => {\n\t\tconst nextSteps = [...steps]\n\t\tconst [movedStep] = nextSteps.splice(from, 1)\n\t\tif (movedStep === undefined) return\n\t\tnextSteps.splice(to, 0, movedStep)\n\t\tconst nextKeys = [...keys]\n\t\tconst [movedKey] = nextKeys.splice(from, 1)\n\t\tif (movedKey !== undefined) nextKeys.splice(to, 0, movedKey)\n\t\temit(nextSteps, nextKeys)\n\t}\n\n\treturn (\n\t\t<div className=\"field-type fb-flow-builder\">\n\t\t\t<div className=\"blocks-field__header\">\n\t\t\t\t<div className=\"blocks-field__header-wrap\">\n\t\t\t\t\t<h3>{typeof label === 'string' ? label : 'Flow'}</h3>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{steps.length === 0 ? (\n\t\t\t\t<p className=\"fb-flow-builder__hint\">\n\t\t\t\t\tNo steps defined. Add at least two steps to enable multi-page flow routing.\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<div className=\"fb-flow-builder__steps\">\n\t\t\t\t\t{steps.map((step, index) => {\n\t\t\t\t\t\tconst key = keys[index]\n\t\t\t\t\t\tif (key === undefined) return null\n\t\t\t\t\t\tconst otherStepIds = allStepIds.filter((id) => id !== step.id)\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<StepCard\n\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\tstepKey={key}\n\t\t\t\t\t\t\t\tstep={step}\n\t\t\t\t\t\t\t\tstepIndex={index}\n\t\t\t\t\t\t\t\tstepCount={steps.length}\n\t\t\t\t\t\t\t\totherStepIds={otherStepIds}\n\t\t\t\t\t\t\t\tfieldNames={fieldNames}\n\t\t\t\t\t\t\t\tconditionTypes={props.conditionTypes}\n\t\t\t\t\t\t\t\tisExpanded={expandedKeys.has(key)}\n\t\t\t\t\t\t\t\tonToggle={() => toggleExpanded(key)}\n\t\t\t\t\t\t\t\tonChange={(next) => updateStep(index, next)}\n\t\t\t\t\t\t\t\tonRemove={() => removeStep(index)}\n\t\t\t\t\t\t\t\tonMoveUp={() => moveStep(index, index - 1)}\n\t\t\t\t\t\t\t\tonMoveDown={() => moveStep(index, index + 1)}\n\t\t\t\t\t\t\t\tonAddAbove={() => insertStep(index)}\n\t\t\t\t\t\t\t\tonAddBelow={() => insertStep(index + 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"fb-flow-builder__actions\">\n\t\t\t\t<Button\n\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\tonClick={addStep}\n\t\t\t\t\tmargin={false}\n\t\t\t\t>\n\t\t\t\t\tAdd step\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FlowBuilder.js","names":[],"sources":["../../src/client/FlowBuilder.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tButton,\n\tCheckboxInput,\n\tCollapsible,\n\tFieldLabel,\n\tReactSelect,\n\ttype ReactSelectOption,\n\tTextInput,\n\tuseField,\n\tuseFormFields,\n} from '@payloadcms/ui'\nimport { Popup, PopupList } from '@payloadcms/ui/elements/Popup'\nimport { MoreIcon } from '@payloadcms/ui/icons/More'\nimport { reduceFieldsToValues } from 'payload/shared'\nimport type { ChangeEvent } from 'react'\nimport { useMemo, useRef, useState } from 'react'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport type { FlowStep, FlowTransition, FormFlow } from '../flow/types'\nimport { ConditionBuilder } from './ConditionBuilder'\nimport type { FieldRow } from './synthesizeClientField'\nimport { toStaticLabel } from './toStaticLabel'\nimport './admin.css'\n\nexport type FlowBuilderProps = {\n\tpath?: string\n\tfield?: { label?: unknown }\n\tlabel?: unknown\n\tconditionTypes: Record<string, ConditionFieldType>\n}\n\nconst extractFieldNames = (data: Record<string, unknown>): string[] => {\n\tconst rows = Array.isArray(data.fields) ? (data.fields as FieldRow[]) : []\n\treturn rows\n\t\t.map((row) => (typeof row.name === 'string' ? row.name.trim() : null))\n\t\t.filter((name): name is string => name !== null && name.length > 0)\n}\n\ntype TransitionRowProps = {\n\ttransition: FlowTransition\n\tstepIds: string[]\n\tconditionTypes: Record<string, ConditionFieldType>\n\tisFirst: boolean\n\tisLast: boolean\n\tonChange: (next: FlowTransition) => void\n\tonRemove: () => void\n\tonMoveUp: () => void\n\tonMoveDown: () => void\n}\n\nconst TransitionRow = ({\n\ttransition,\n\tstepIds,\n\tconditionTypes,\n\tisFirst,\n\tisLast,\n\tonChange,\n\tonRemove,\n\tonMoveUp,\n\tonMoveDown,\n}: TransitionRowProps) => {\n\tconst toOptions: ReactSelectOption[] = stepIds.map((id) => ({ label: id, value: id }))\n\n\tconst handleGotoChange = (selected: ReactSelectOption | ReactSelectOption[]) => {\n\t\tconst chosen = Array.isArray(selected) ? selected[0] : selected\n\t\tif (chosen) onChange({ ...transition, to: chosen.value as string })\n\t}\n\n\treturn (\n\t\t<div className=\"fb-flow-transition\">\n\t\t\t<div className=\"fb-flow-transition__header\">\n\t\t\t\t<span className=\"fb-flow-transition__label\">go to</span>\n\t\t\t\t<div className=\"fb-flow-transition__goto\">\n\t\t\t\t\t<ReactSelect\n\t\t\t\t\t\toptions={toOptions}\n\t\t\t\t\t\tvalue={toOptions.find((o) => o.value === transition.to) ?? undefined}\n\t\t\t\t\t\tplaceholder=\"Select step…\"\n\t\t\t\t\t\tisClearable={false}\n\t\t\t\t\t\tonChange={handleGotoChange}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"fb-flow-transition__actions\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"fb-flow__move-btn\"\n\t\t\t\t\t\tonClick={onMoveUp}\n\t\t\t\t\t\tdisabled={isFirst}\n\t\t\t\t\t\taria-label=\"Move transition up\"\n\t\t\t\t\t>\n\t\t\t\t\t\t↑\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"fb-flow__move-btn\"\n\t\t\t\t\t\tonClick={onMoveDown}\n\t\t\t\t\t\tdisabled={isLast}\n\t\t\t\t\t\taria-label=\"Move transition down\"\n\t\t\t\t\t>\n\t\t\t\t\t\t↓\n\t\t\t\t\t</button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\ticon=\"x\"\n\t\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t\taria-label=\"Remove transition\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<span className=\"fb-flow-transition__when-label\">when</span>\n\t\t\t<div className=\"fb-flow-transition__condition\">\n\t\t\t\t<ConditionBuilder\n\t\t\t\t\tvalue={transition.when}\n\t\t\t\t\tonChange={(when) => onChange({ ...transition, when })}\n\t\t\t\t\tconditionTypes={conditionTypes}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\ntype StepCardProps = {\n\tstep: FlowStep\n\tstepKey: string\n\tstepIndex: number\n\tstepCount: number\n\totherStepIds: string[]\n\tfieldNames: string[]\n\tconditionTypes: Record<string, ConditionFieldType>\n\tisExpanded: boolean\n\tonToggle: () => void\n\tonChange: (next: FlowStep) => void\n\tonRemove: () => void\n\tonMoveUp: () => void\n\tonMoveDown: () => void\n\tonAddAbove: () => void\n\tonAddBelow: () => void\n}\n\nconst StepCard = ({\n\tstep,\n\tstepKey,\n\tstepIndex,\n\tstepCount,\n\totherStepIds,\n\tfieldNames,\n\tconditionTypes,\n\tisExpanded,\n\tonToggle,\n\tonChange,\n\tonRemove,\n\tonMoveUp,\n\tonMoveDown,\n\tonAddAbove,\n\tonAddBelow,\n}: StepCardProps) => {\n\tconst nextOptions: ReactSelectOption[] = [\n\t\t{ label: '— terminal —', value: '' },\n\t\t...otherStepIds.map((id) => ({ label: id, value: id })),\n\t]\n\n\tconst transitions = step.transitions ?? []\n\n\t// Stable per-transition keys, mirroring the step-level scheme so transition reorder/remove\n\t// reconciles by identity rather than array position.\n\tconst tKeysRef = useRef<string[]>([])\n\tif (tKeysRef.current.length !== transitions.length) {\n\t\ttKeysRef.current = transitions.map((_, i) => tKeysRef.current[i] ?? crypto.randomUUID())\n\t}\n\tconst tKeys = tKeysRef.current\n\n\tconst addTransition = () => {\n\t\tconst firstOtherStep = otherStepIds[0]\n\t\tif (!firstOtherStep) return\n\t\ttKeysRef.current = [...tKeys, crypto.randomUUID()]\n\t\tonChange({\n\t\t\t...step,\n\t\t\ttransitions: [...transitions, { when: {}, to: firstOtherStep }],\n\t\t})\n\t}\n\n\tconst updateTransition = (index: number, next: FlowTransition) => {\n\t\tconst updated = [...transitions]\n\t\tupdated[index] = next\n\t\tonChange({ ...step, transitions: updated })\n\t}\n\n\tconst removeTransition = (index: number) => {\n\t\ttKeysRef.current = tKeys.filter((_, i) => i !== index)\n\t\tconst updated = transitions.filter((_, i) => i !== index)\n\t\tonChange({ ...step, transitions: updated.length > 0 ? updated : undefined })\n\t}\n\n\tconst moveTransition = (from: number, to: number) => {\n\t\tconst updated = [...transitions]\n\t\tconst [item] = updated.splice(from, 1)\n\t\tif (item !== undefined) updated.splice(to, 0, item)\n\t\tconst nextKeys = [...tKeys]\n\t\tconst [movedKey] = nextKeys.splice(from, 1)\n\t\tif (movedKey !== undefined) nextKeys.splice(to, 0, movedKey)\n\t\ttKeysRef.current = nextKeys\n\t\tonChange({ ...step, transitions: updated })\n\t}\n\n\tconst handleNextChange = (selected: ReactSelectOption | ReactSelectOption[]) => {\n\t\tconst chosen = Array.isArray(selected) ? selected[0] : selected\n\t\tconst next = (chosen?.value as string | undefined) || undefined\n\t\tonChange({ ...step, next })\n\t}\n\n\tconst stepHeader = (\n\t\t<div className=\"fb-flow-step__header-content\">\n\t\t\t<span className=\"fb-flow-step__index\">Step {stepIndex + 1}</span>\n\t\t\t<span className=\"fb-flow-step__id-preview\">\n\t\t\t\t{step.id.length > 0 ? step.id : <em>no id</em>}\n\t\t\t</span>\n\t\t</div>\n\t)\n\n\treturn (\n\t\t<Collapsible\n\t\t\theader={stepHeader}\n\t\t\tactions={\n\t\t\t\t<Popup\n\t\t\t\t\tbutton={<MoreIcon />}\n\t\t\t\t\tbuttonType=\"default\"\n\t\t\t\t\thorizontalAlign=\"right\"\n\t\t\t\t\tsize=\"fit-content\"\n\t\t\t\t\tverticalAlign=\"bottom\"\n\t\t\t\t>\n\t\t\t\t\t<PopupList.ButtonGroup>\n\t\t\t\t\t\t<PopupList.Button onClick={onAddAbove}>Add above</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onAddBelow}>Add below</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onMoveUp} disabled={stepIndex === 0}>\n\t\t\t\t\t\t\tMove up\n\t\t\t\t\t\t</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onMoveDown} disabled={stepIndex === stepCount - 1}>\n\t\t\t\t\t\t\tMove down\n\t\t\t\t\t\t</PopupList.Button>\n\t\t\t\t\t\t<PopupList.Button onClick={onRemove}>Remove</PopupList.Button>\n\t\t\t\t\t</PopupList.ButtonGroup>\n\t\t\t\t</Popup>\n\t\t\t}\n\t\t\tisCollapsed={!isExpanded}\n\t\t\tonToggle={() => onToggle()}\n\t\t\tclassName=\"fb-flow-step\"\n\t\t>\n\t\t\t<div className=\"fb-flow-step__body\">\n\t\t\t\t<TextInput\n\t\t\t\t\tpath={`fb-flow-step-${stepKey}-id`}\n\t\t\t\t\tlabel=\"Step ID\"\n\t\t\t\t\tvalue={step.id}\n\t\t\t\t\tplaceholder=\"e.g. intro\"\n\t\t\t\t\tonChange={(e: ChangeEvent<HTMLInputElement>) => onChange({ ...step, id: e.target.value })}\n\t\t\t\t/>\n\n\t\t\t\t<TextInput\n\t\t\t\t\tpath={`fb-flow-step-${stepKey}-title`}\n\t\t\t\t\tlabel=\"Title\"\n\t\t\t\t\tvalue={step.title ?? ''}\n\t\t\t\t\tplaceholder=\"Optional display title\"\n\t\t\t\t\tonChange={(e: ChangeEvent<HTMLInputElement>) =>\n\t\t\t\t\t\tonChange({ ...step, title: e.target.value.length > 0 ? e.target.value : undefined })\n\t\t\t\t\t}\n\t\t\t\t/>\n\n\t\t\t\t<div className=\"fb-flow-step__row\">\n\t\t\t\t\t<FieldLabel label=\"Fields\" />\n\t\t\t\t\t{fieldNames.length === 0 ? (\n\t\t\t\t\t\t<p className=\"fb-flow-step__hint\">No fields defined on the form yet.</p>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"fb-flow-step__field-picker\">\n\t\t\t\t\t\t\t{fieldNames.map((name) => (\n\t\t\t\t\t\t\t\t<CheckboxInput\n\t\t\t\t\t\t\t\t\tkey={name}\n\t\t\t\t\t\t\t\t\tlabel={name}\n\t\t\t\t\t\t\t\t\tchecked={step.fields.includes(name)}\n\t\t\t\t\t\t\t\t\tonToggle={(e) => {\n\t\t\t\t\t\t\t\t\t\tconst fields = e.target.checked\n\t\t\t\t\t\t\t\t\t\t\t? [...step.fields, name]\n\t\t\t\t\t\t\t\t\t\t\t: step.fields.filter((f) => f !== name)\n\t\t\t\t\t\t\t\t\t\tonChange({ ...step, fields })\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))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"fb-flow-step__row\">\n\t\t\t\t\t<FieldLabel label=\"Default next\" />\n\t\t\t\t\t<ReactSelect\n\t\t\t\t\t\toptions={nextOptions}\n\t\t\t\t\t\tvalue={\n\t\t\t\t\t\t\tstep.next\n\t\t\t\t\t\t\t\t? (nextOptions.find((o) => o.value === step.next) ?? undefined)\n\t\t\t\t\t\t\t\t: (nextOptions[0] ?? undefined)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tisClearable={false}\n\t\t\t\t\t\tonChange={handleNextChange}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"fb-flow-step__transitions\">\n\t\t\t\t\t<div className=\"fb-flow-step__transitions-header\">\n\t\t\t\t\t\t<FieldLabel label=\"Conditional transitions\" />\n\t\t\t\t\t\t<span className=\"fb-flow-step__hint\">(first match wins)</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{transitions.map((transition, tIndex) => {\n\t\t\t\t\t\tconst tKey = tKeys[tIndex]\n\t\t\t\t\t\tif (tKey === undefined) return null\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<TransitionRow\n\t\t\t\t\t\t\t\tkey={tKey}\n\t\t\t\t\t\t\t\ttransition={transition}\n\t\t\t\t\t\t\t\tstepIds={otherStepIds}\n\t\t\t\t\t\t\t\tconditionTypes={conditionTypes}\n\t\t\t\t\t\t\t\tisFirst={tIndex === 0}\n\t\t\t\t\t\t\t\tisLast={tIndex === transitions.length - 1}\n\t\t\t\t\t\t\t\tonChange={(next) => updateTransition(tIndex, next)}\n\t\t\t\t\t\t\t\tonRemove={() => removeTransition(tIndex)}\n\t\t\t\t\t\t\t\tonMoveUp={() => moveTransition(tIndex, tIndex - 1)}\n\t\t\t\t\t\t\t\tonMoveDown={() => moveTransition(tIndex, tIndex + 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t\t<Button\n\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\t\tonClick={addTransition}\n\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t>\n\t\t\t\t\t\tAdd transition\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\nexport const FlowBuilder = (props: FlowBuilderProps) => {\n\tconst { setValue, value } = useField<FormFlow | undefined>()\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\tconst [expandedKeys, setExpandedKeys] = useState<Set<string>>(new Set())\n\n\tconst fieldNamesJson = useFormFields(([fields]) =>\n\t\tJSON.stringify(extractFieldNames(reduceFieldsToValues(fields, true) as Record<string, unknown>))\n\t)\n\tconst fieldNames = useMemo(() => JSON.parse(fieldNamesJson) as string[], [fieldNamesJson])\n\n\tconst steps = value?.steps ?? []\n\tconst allStepIds = steps.map((s) => s.id).filter((id) => id.length > 0)\n\n\t// Stable per-step keys, decoupled from the user-editable step.id. Reorder/insert/remove\n\t// reconcile by identity so the focused Step ID input and each step's expand state stay with\n\t// their step instead of jumping to whichever step now sits at that array position.\n\tconst keysRef = useRef<string[]>([])\n\tif (keysRef.current.length !== steps.length) {\n\t\tkeysRef.current = steps.map((_, i) => keysRef.current[i] ?? crypto.randomUUID())\n\t}\n\tconst keys = keysRef.current\n\n\tconst toggleExpanded = (key: string) => {\n\t\tsetExpandedKeys((prev) => {\n\t\t\tconst next = new Set(prev)\n\t\t\tif (next.has(key)) next.delete(key)\n\t\t\telse next.add(key)\n\t\t\treturn next\n\t\t})\n\t}\n\n\tconst emit = (nextSteps: FlowStep[], nextKeys: string[]) => {\n\t\tkeysRef.current = nextKeys\n\t\tsetValue({ steps: nextSteps } as FormFlow)\n\t}\n\n\tconst insertStep = (at: number) => {\n\t\tconst key = crypto.randomUUID()\n\t\tconst nextSteps = [...steps]\n\t\tnextSteps.splice(at, 0, { id: crypto.randomUUID(), fields: [] })\n\t\tconst nextKeys = [...keys]\n\t\tnextKeys.splice(at, 0, key)\n\t\temit(nextSteps, nextKeys)\n\t\tsetExpandedKeys((prev) => new Set(prev).add(key))\n\t}\n\n\tconst addStep = () => insertStep(steps.length)\n\n\tconst updateStep = (index: number, next: FlowStep) => {\n\t\temit(\n\t\t\tsteps.map((s, i) => (i === index ? next : s)),\n\t\t\tkeys\n\t\t)\n\t}\n\n\tconst removeStep = (index: number) => {\n\t\tconst removedKey = keys[index]\n\t\temit(\n\t\t\tsteps.filter((_, i) => i !== index),\n\t\t\tkeys.filter((_, i) => i !== index)\n\t\t)\n\t\tif (removedKey !== undefined) {\n\t\t\tsetExpandedKeys((prev) => {\n\t\t\t\tconst next = new Set(prev)\n\t\t\t\tnext.delete(removedKey)\n\t\t\t\treturn next\n\t\t\t})\n\t\t}\n\t}\n\n\tconst moveStep = (from: number, to: number) => {\n\t\tconst nextSteps = [...steps]\n\t\tconst [movedStep] = nextSteps.splice(from, 1)\n\t\tif (movedStep === undefined) return\n\t\tnextSteps.splice(to, 0, movedStep)\n\t\tconst nextKeys = [...keys]\n\t\tconst [movedKey] = nextKeys.splice(from, 1)\n\t\tif (movedKey !== undefined) nextKeys.splice(to, 0, movedKey)\n\t\temit(nextSteps, nextKeys)\n\t}\n\n\treturn (\n\t\t<div className=\"field-type fb-flow-builder\">\n\t\t\t<div className=\"blocks-field__header\">\n\t\t\t\t<div className=\"blocks-field__header-wrap\">\n\t\t\t\t\t<h3>{typeof label === 'string' ? label : 'Flow'}</h3>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{steps.length === 0 ? (\n\t\t\t\t<p className=\"fb-flow-builder__hint\">\n\t\t\t\t\tNo steps defined. Add at least two steps to enable multi-page flow routing.\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<div className=\"fb-flow-builder__steps\">\n\t\t\t\t\t{steps.map((step, index) => {\n\t\t\t\t\t\tconst key = keys[index]\n\t\t\t\t\t\tif (key === undefined) return null\n\t\t\t\t\t\tconst otherStepIds = allStepIds.filter((id) => id !== step.id)\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<StepCard\n\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\tstepKey={key}\n\t\t\t\t\t\t\t\tstep={step}\n\t\t\t\t\t\t\t\tstepIndex={index}\n\t\t\t\t\t\t\t\tstepCount={steps.length}\n\t\t\t\t\t\t\t\totherStepIds={otherStepIds}\n\t\t\t\t\t\t\t\tfieldNames={fieldNames}\n\t\t\t\t\t\t\t\tconditionTypes={props.conditionTypes}\n\t\t\t\t\t\t\t\tisExpanded={expandedKeys.has(key)}\n\t\t\t\t\t\t\t\tonToggle={() => toggleExpanded(key)}\n\t\t\t\t\t\t\t\tonChange={(next) => updateStep(index, next)}\n\t\t\t\t\t\t\t\tonRemove={() => removeStep(index)}\n\t\t\t\t\t\t\t\tonMoveUp={() => moveStep(index, index - 1)}\n\t\t\t\t\t\t\t\tonMoveDown={() => moveStep(index, index + 1)}\n\t\t\t\t\t\t\t\tonAddAbove={() => insertStep(index)}\n\t\t\t\t\t\t\t\tonAddBelow={() => insertStep(index + 1)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"fb-flow-builder__actions\">\n\t\t\t\t<Button\n\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\tonClick={addStep}\n\t\t\t\t\tmargin={false}\n\t\t\t\t>\n\t\t\t\t\tAdd step\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { toStaticLabel } from "./toStaticLabel.js";
|
|
2
3
|
import { ConditionBuilder } from "./ConditionBuilder.js";
|
|
3
4
|
import { FieldLabel, useField } from "@payloadcms/ui";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
6
|
//#region src/client/FormConditionField.tsx
|
|
6
|
-
const toStaticLabel = (label) => {
|
|
7
|
-
if (typeof label === "string") return label;
|
|
8
|
-
if (label && typeof label === "object") return label;
|
|
9
|
-
};
|
|
10
7
|
/**
|
|
11
8
|
* The `Field` mounted on a form field's `visibleWhen`/`validateWhen` json column. Binds the field's own
|
|
12
9
|
* path with `useField<Where>()` (the QueryPresetsWhereField precedent) and renders the native builder.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormConditionField.js","names":[],"sources":["../../src/client/FormConditionField.tsx"],"sourcesContent":["'use client'\n\nimport { FieldLabel, useField } from '@payloadcms/ui'\nimport type {
|
|
1
|
+
{"version":3,"file":"FormConditionField.js","names":[],"sources":["../../src/client/FormConditionField.tsx"],"sourcesContent":["'use client'\n\nimport { FieldLabel, useField } from '@payloadcms/ui'\nimport type { Where } from 'payload'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport { ConditionBuilder } from './ConditionBuilder'\nimport { toStaticLabel } from './toStaticLabel'\n\n/** Props: standard JSON field client props plus the `conditionTypes` map we pass via clientProps. */\nexport type FormConditionFieldProps = {\n\tpath?: string\n\tfield?: { label?: unknown; name?: string }\n\tlabel?: unknown\n\tconditionTypes: Record<string, ConditionFieldType>\n}\n\n/**\n * The `Field` mounted on a form field's `visibleWhen`/`validateWhen` json column. Binds the field's own\n * path with `useField<Where>()` (the QueryPresetsWhereField precedent) and renders the native builder.\n */\nexport const FormConditionField = (props: FormConditionFieldProps) => {\n\tconst { path, setValue, value } = useField<Where>()\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\treturn (\n\t\t<div className=\"field-type\" style={{ marginBlockEnd: '1rem' }}>\n\t\t\t<FieldLabel label={label} path={path} />\n\t\t\t<ConditionBuilder\n\t\t\t\tvalue={value ?? undefined}\n\t\t\t\tonChange={setValue}\n\t\t\t\tconditionTypes={props.conditionTypes}\n\t\t\t\tselfName={props.field?.name}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;;;;;AAoBA,MAAa,sBAAsB,UAAmC;CACrE,MAAM,EAAE,MAAM,UAAU,UAAU,SAAgB;CAElD,OACC,qBAAC,OAAD;EAAK,WAAU;EAAa,OAAO,EAAE,gBAAgB,OAAO;YAA5D,CACC,oBAAC,YAAD;GAAY,OAHA,cAAc,MAAM,OAAO,SAAS,MAAM,KAG/B;GAAS;EAAO,CAAA,GACvC,oBAAC,kBAAD;GACC,OAAO,SAAS,KAAA;GAChB,UAAU;GACV,gBAAgB,MAAM;GACtB,UAAU,MAAM,OAAO;EACvB,CAAA,CACG;;AAEP"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/* Admin panel styles, imported by the client components themselves so consumers
|
|
2
|
+
* get styled admin UI without touching their layout. Relies on Payload theme variables. */
|
|
3
|
+
|
|
4
|
+
/* Flow builder */
|
|
5
|
+
.fb-flow-builder {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 0.5rem;
|
|
9
|
+
margin-block-end: 1rem;
|
|
10
|
+
}
|
|
11
|
+
.fb-flow-builder__hint {
|
|
12
|
+
color: var(--theme-elevation-400);
|
|
13
|
+
font-size: 0.875rem;
|
|
14
|
+
margin: 0;
|
|
15
|
+
}
|
|
16
|
+
.fb-flow-builder__steps {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 0.5rem;
|
|
20
|
+
}
|
|
21
|
+
.fb-flow-builder__actions {
|
|
22
|
+
margin-block-start: 0.25rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.fb-flow-step__header-content {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 0.5rem;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
.fb-flow-step__index {
|
|
32
|
+
font-size: 0.6875rem;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
text-transform: uppercase;
|
|
35
|
+
letter-spacing: 0.04em;
|
|
36
|
+
color: var(--theme-elevation-400);
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
}
|
|
39
|
+
.fb-flow-step__id-preview {
|
|
40
|
+
font-size: 0.875rem;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
}
|
|
45
|
+
.fb-flow-step__id-preview em {
|
|
46
|
+
color: var(--theme-elevation-400);
|
|
47
|
+
font-style: italic;
|
|
48
|
+
}
|
|
49
|
+
.fb-flow-step__body {
|
|
50
|
+
padding: 0.75rem;
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 0.25rem;
|
|
54
|
+
}
|
|
55
|
+
.fb-flow-step__row {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: 0.25rem;
|
|
59
|
+
}
|
|
60
|
+
.fb-flow-step__hint {
|
|
61
|
+
font-size: 0.75rem;
|
|
62
|
+
color: var(--theme-elevation-400);
|
|
63
|
+
margin: 0;
|
|
64
|
+
}
|
|
65
|
+
.fb-flow-step__field-picker {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-wrap: wrap;
|
|
68
|
+
gap: 0.25rem 1.25rem;
|
|
69
|
+
padding-block: 0.25rem;
|
|
70
|
+
}
|
|
71
|
+
.fb-flow-step__transitions {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
gap: 0.5rem;
|
|
75
|
+
padding-block-start: 0.5rem;
|
|
76
|
+
}
|
|
77
|
+
.fb-flow-step__transitions-header {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: baseline;
|
|
80
|
+
gap: 0.5rem;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.fb-flow-transition {
|
|
84
|
+
border: 1px solid var(--theme-border-color);
|
|
85
|
+
border-radius: var(--style-radius-s);
|
|
86
|
+
padding: 0.5rem 0.75rem;
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
gap: 0.5rem;
|
|
90
|
+
}
|
|
91
|
+
.fb-flow-transition__header {
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
gap: 0.5rem;
|
|
95
|
+
}
|
|
96
|
+
.fb-flow-transition__label {
|
|
97
|
+
font-size: 0.6875rem;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
text-transform: uppercase;
|
|
100
|
+
letter-spacing: 0.04em;
|
|
101
|
+
color: var(--theme-elevation-400);
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
}
|
|
104
|
+
.fb-flow-transition__goto {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
}
|
|
108
|
+
.fb-flow-transition__actions {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 0.25rem;
|
|
112
|
+
flex-shrink: 0;
|
|
113
|
+
}
|
|
114
|
+
.fb-flow-transition__when-label {
|
|
115
|
+
font-size: 0.6875rem;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.04em;
|
|
119
|
+
color: var(--theme-elevation-400);
|
|
120
|
+
}
|
|
121
|
+
.fb-flow-transition__condition {
|
|
122
|
+
padding-inline-start: 0.25rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Condition builder */
|
|
126
|
+
.fb-condition-builder {
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
gap: 0.5rem;
|
|
130
|
+
}
|
|
131
|
+
.fb-condition-builder__hint {
|
|
132
|
+
color: var(--theme-elevation-400);
|
|
133
|
+
font-size: 0.875rem;
|
|
134
|
+
margin: 0;
|
|
135
|
+
}
|
|
136
|
+
.fb-condition-builder__group {
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
gap: 0.375rem;
|
|
140
|
+
padding: 0.75rem;
|
|
141
|
+
border: 1px solid var(--theme-border-color);
|
|
142
|
+
border-radius: var(--style-radius-m);
|
|
143
|
+
}
|
|
144
|
+
.fb-condition-builder__or-label {
|
|
145
|
+
display: inline-block;
|
|
146
|
+
font-size: 11px;
|
|
147
|
+
font-weight: 500;
|
|
148
|
+
text-transform: uppercase;
|
|
149
|
+
margin-block: 0.125rem;
|
|
150
|
+
}
|
|
151
|
+
.fb-condition-builder__and-row {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
gap: 0.25rem;
|
|
155
|
+
}
|
|
156
|
+
.fb-condition-builder__and-label {
|
|
157
|
+
font-size: 10px;
|
|
158
|
+
font-weight: 500;
|
|
159
|
+
text-transform: uppercase;
|
|
160
|
+
padding-inline-start: 0.125rem;
|
|
161
|
+
}
|
|
162
|
+
.fb-condition-builder__add-condition {
|
|
163
|
+
align-self: flex-start;
|
|
164
|
+
margin-block-start: 0.25rem;
|
|
165
|
+
}
|
|
166
|
+
.fb-condition-builder__actions {
|
|
167
|
+
display: flex;
|
|
168
|
+
gap: 0.5rem;
|
|
169
|
+
margin-top: 0.5rem;
|
|
170
|
+
}
|
|
171
|
+
.fb-condition-row {
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: flex-start;
|
|
174
|
+
gap: 0.5rem;
|
|
175
|
+
flex-wrap: nowrap;
|
|
176
|
+
}
|
|
177
|
+
.fb-condition-row__field {
|
|
178
|
+
flex: 1 1 9rem;
|
|
179
|
+
min-width: 0;
|
|
180
|
+
}
|
|
181
|
+
.fb-condition-row__operator {
|
|
182
|
+
flex: 0 1 9rem;
|
|
183
|
+
min-width: 0;
|
|
184
|
+
}
|
|
185
|
+
.fb-condition-row__value {
|
|
186
|
+
flex: 1 1 9rem;
|
|
187
|
+
min-width: 0;
|
|
188
|
+
}
|
|
189
|
+
.fb-condition-row__remove {
|
|
190
|
+
flex-shrink: 0;
|
|
191
|
+
align-self: center;
|
|
192
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/client/toStaticLabel.ts
|
|
2
|
+
/**
|
|
3
|
+
* Narrows the untyped `label` a custom Field component receives (via `field.label` or the
|
|
4
|
+
* top-level prop) to a renderable static label, dropping functions and other non-static shapes.
|
|
5
|
+
*/
|
|
6
|
+
const toStaticLabel = (label) => {
|
|
7
|
+
if (typeof label === "string") return label;
|
|
8
|
+
if (label && typeof label === "object") return label;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { toStaticLabel };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=toStaticLabel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toStaticLabel.js","names":[],"sources":["../../src/client/toStaticLabel.ts"],"sourcesContent":["import type { StaticLabel } from 'payload'\n\n/**\n * Narrows the untyped `label` a custom Field component receives (via `field.label` or the\n * top-level prop) to a renderable static label, dropping functions and other non-static shapes.\n */\nexport const toStaticLabel = (label: unknown): StaticLabel | undefined => {\n\tif (typeof label === 'string') {\n\t\treturn label\n\t}\n\tif (label && typeof label === 'object') {\n\t\treturn label as Record<string, string>\n\t}\n\treturn undefined\n}\n"],"mappings":";;;;;AAMA,MAAa,iBAAiB,UAA4C;CACzE,IAAI,OAAO,UAAU,UACpB,OAAO;CAER,IAAI,SAAS,OAAO,UAAU,UAC7B,OAAO;AAGT"}
|
|
@@ -42,7 +42,8 @@ const makeAfterChange = (args) => async ({ doc, operation, req }) => {
|
|
|
42
42
|
registry: args.actionRegistry,
|
|
43
43
|
payload,
|
|
44
44
|
req,
|
|
45
|
-
hasRunner: args.hasRunner
|
|
45
|
+
hasRunner: args.hasRunner,
|
|
46
|
+
richText: args.richText
|
|
46
47
|
});
|
|
47
48
|
try {
|
|
48
49
|
await resolveEventSink(args.events).emit({
|
|
@@ -59,7 +60,7 @@ const makeAfterChange = (args) => async ({ doc, operation, req }) => {
|
|
|
59
60
|
}
|
|
60
61
|
return doc;
|
|
61
62
|
};
|
|
62
|
-
const buildSubmissionsCollection = ({ registry, ruleRegistry, consentRegistry, actionRegistry = /* @__PURE__ */ new Map(), events, hasRunner = false, uploadSlug, spam, showRawFields = false, overrides }) => {
|
|
63
|
+
const buildSubmissionsCollection = ({ registry, ruleRegistry, consentRegistry, actionRegistry = /* @__PURE__ */ new Map(), events, hasRunner = false, richText, uploadSlug, spam, showRawFields = false, overrides }) => {
|
|
63
64
|
const defaultFields = [
|
|
64
65
|
{
|
|
65
66
|
name: "form",
|
|
@@ -145,7 +146,8 @@ const buildSubmissionsCollection = ({ registry, ruleRegistry, consentRegistry, a
|
|
|
145
146
|
afterChange: [makeAfterChange({
|
|
146
147
|
actionRegistry,
|
|
147
148
|
events,
|
|
148
|
-
hasRunner
|
|
149
|
+
hasRunner,
|
|
150
|
+
richText
|
|
149
151
|
}), ...overrides?.hooks?.afterChange ?? []]
|
|
150
152
|
},
|
|
151
153
|
fields: overrides?.fields ? overrides.fields({ defaultFields }) : defaultFields
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formSubmissions.js","names":[],"sources":["../../src/collections/formSubmissions.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionConfig, Field } from 'payload'\nimport { dispatchActions } from '../actions/dispatch'\nimport type { ActionRegistry } from '../actions/registry'\nimport type { ActionInstance } from '../actions/runActions'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport { resolveEventSink } from '../events/resolveEventSink'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { isLoggedIn } from '../plugin/access'\nimport type { CollectionOverrides } from '../plugin/collectionOverrides'\nimport { buildSpamGuard } from '../spam/spamGuard'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { validateSubmission } from '../submissions/validateSubmission'\nimport { keys } from '../translations/keys'\nimport { labelForKey } from '../translations/server'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { FORMS_SLUG } from './forms'\n\nexport const FORM_SUBMISSIONS_SLUG = 'form-submissions'\n\ntype BuildSubmissionsCollectionArgs = {\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tactionRegistry?: ActionRegistry\n\tevents?: FormEventSink\n\t/** Whether a job runner is likely present; gates the queued vs bounded-inline dispatch path. */\n\thasRunner?: boolean\n\t/** Upload collection slug for file fields without an explicit `relationTo`. */\n\tuploadSlug?: string\n\t/** Resolved spam config; when active, prepends the spam guard before validation. `false` disables it. */\n\tspam?: ResolvedSpamConfig | false\n\t/**\n\t * When `true`, shows the raw `values`, `descriptors`, and `consent` JSON fields in the admin UI.\n\t * Default `false` — they are fully represented by the `SubmissionAnswers` UI component.\n\t */\n\tshowRawFields?: boolean\n\toverrides?: CollectionOverrides\n}\n\nconst formIdOf = (form: unknown): number | string | undefined => {\n\tif (typeof form === 'number' || typeof form === 'string') {\n\t\treturn form\n\t}\n\tif (form && typeof form === 'object' && 'id' in form) {\n\t\tconst id = (form as { id: unknown }).id\n\t\tif (typeof id === 'number' || typeof id === 'string') {\n\t\t\treturn id\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * On a completed submission create, dispatch the form's post-submit actions and emit `submission.created`.\n * Both are side effects on an already-written row, so the whole body is wrapped: nothing it does can throw\n * past the hook (a failed action or sink must never fail the submission write). Dispatch is itself bounded\n * and non-throwing; the inline fallback is awaited so the bound caps the added latency, while the queued\n * path returns immediately. The form's `actions` are null-guarded for legacy rows created before the field\n * existed.\n */\nconst makeAfterChange =\n\t(args: {\n\t\tactionRegistry: ActionRegistry\n\t\tevents?: FormEventSink\n\t\thasRunner: boolean\n\t}): CollectionAfterChangeHook =>\n\tasync ({ doc, operation, req }) => {\n\t\tif (operation !== 'create' || (doc.status != null && doc.status !== 'complete')) {\n\t\t\treturn doc\n\t\t}\n\t\tconst { payload } = req\n\t\ttry {\n\t\t\tconst formId = formIdOf(doc.form)\n\t\t\tif (formId == null) {\n\t\t\t\treturn doc\n\t\t\t}\n\t\t\tconst form = await payload\n\t\t\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t\t\t.catch(() => null)\n\n\t\t\tawait dispatchActions({\n\t\t\t\tactions: (form?.actions ?? null) as ActionInstance[] | null,\n\t\t\t\tformId,\n\t\t\t\tsubmissionId: doc.id as number | string,\n\t\t\t\tregistry: args.actionRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\thasRunner: args.hasRunner,\n\t\t\t})\n\n\t\t\ttry {\n\t\t\t\tawait resolveEventSink(args.events).emit({\n\t\t\t\t\ttype: 'submission.created',\n\t\t\t\t\tformId: String(formId),\n\t\t\t\t\tsubmissionId: String(doc.id),\n\t\t\t\t\tat: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tpayload.logger?.error(\n\t\t\t\t\t`@10x-media/form-builder: submission.created sink threw: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tpayload.logger?.error(\n\t\t\t\t`@10x-media/form-builder: afterChange dispatch failed for submission ${String(doc.id)}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`\n\t\t\t)\n\t\t}\n\t\treturn doc\n\t}\n\nexport const buildSubmissionsCollection = ({\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tactionRegistry = new Map(),\n\tevents,\n\thasRunner = false,\n\tuploadSlug,\n\tspam,\n\tshowRawFields = false,\n\toverrides,\n}: BuildSubmissionsCollectionArgs): CollectionConfig => {\n\tconst defaultFields: Field[] = [\n\t\t{ name: 'form', type: 'relationship', relationTo: FORMS_SLUG, required: true },\n\t\t{\n\t\t\tname: 'status',\n\t\t\ttype: 'select',\n\t\t\tdefaultValue: 'complete',\n\t\t\toptions: [\n\t\t\t\t{ label: labelForKey(keys.statusComplete), value: 'complete' },\n\t\t\t\t{ label: labelForKey(keys.statusPartial), value: 'partial' },\n\t\t\t],\n\t\t\t// Defense-in-depth at the REST layer: anonymous clients cannot set status via the API.\n\t\t\t// The validateSubmission hook also forces 'complete' server-side, so this covers both paths.\n\t\t\taccess: { create: isLoggedIn, update: isLoggedIn },\n\t\t},\n\t\t// answers UI appears first so it is the dominant view when opening a submission document.\n\t\t{\n\t\t\tname: 'answers',\n\t\t\ttype: 'ui',\n\t\t\tadmin: {\n\t\t\t\tcomponents: { Field: '@10x-media/form-builder/rsc#SubmissionAnswers' },\n\t\t\t},\n\t\t},\n\t\t{ name: 'locale', type: 'text' },\n\t\t{ name: 'values', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'descriptors', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'consent', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'meta', type: 'json' },\n\t]\n\n\treturn {\n\t\t...(overrides ?? {}),\n\t\tslug: FORM_SUBMISSIONS_SLUG,\n\t\tlabels: {\n\t\t\tsingular: labelForKey(keys.collectionSubmissionSingular),\n\t\t\tplural: labelForKey(keys.collectionSubmissionPlural),\n\t\t\t...(overrides?.labels ?? {}),\n\t\t},\n\t\tadmin: { group: 'Forms', ...(overrides?.admin ?? {}) },\n\t\taccess: {\n\t\t\tcreate: () => true,\n\t\t\tread: isLoggedIn,\n\t\t\tupdate: () => false,\n\t\t\t...(overrides?.access ?? {}),\n\t\t},\n\t\thooks: {\n\t\t\t...(overrides?.hooks ?? {}),\n\t\t\t// Spam guard + validateSubmission must remain first: they enforce the security invariant\n\t\t\t// that anonymous callers cannot bypass post-submit actions or supply a forged status.\n\t\t\t// Consumer beforeValidate hooks are appended after so they run on already-validated data.\n\t\t\tbeforeValidate: [\n\t\t\t\t...(spam ? [buildSpamGuard(spam)] : []),\n\t\t\t\tvalidateSubmission({ registry, ruleRegistry, consentRegistry, uploadSlug }),\n\t\t\t\t...(overrides?.hooks?.beforeValidate ?? []),\n\t\t\t],\n\t\t\tafterChange: [\n\t\t\t\tmakeAfterChange({ actionRegistry, events, hasRunner }),\n\t\t\t\t...(overrides?.hooks?.afterChange ?? []),\n\t\t\t],\n\t\t},\n\t\tfields: overrides?.fields ? overrides.fields({ defaultFields }) : defaultFields,\n\t}\n}\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"formSubmissions.js","names":[],"sources":["../../src/collections/formSubmissions.ts"],"sourcesContent":["import type { CollectionAfterChangeHook, CollectionConfig, Field } from 'payload'\nimport type { RichTextBodyOption } from '../actions/body/serializeBody'\nimport { dispatchActions } from '../actions/dispatch'\nimport type { ActionRegistry } from '../actions/registry'\nimport type { ActionInstance } from '../actions/runActions'\nimport type { ConsentSourceRegistry } from '../consent/registry'\nimport { resolveEventSink } from '../events/resolveEventSink'\nimport type { FormEventSink } from '../events/types'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport { isLoggedIn } from '../plugin/access'\nimport type { CollectionOverrides } from '../plugin/collectionOverrides'\nimport { buildSpamGuard } from '../spam/spamGuard'\nimport type { ResolvedSpamConfig } from '../spam/types'\nimport { validateSubmission } from '../submissions/validateSubmission'\nimport { keys } from '../translations/keys'\nimport { labelForKey } from '../translations/server'\nimport type { ValidationRuleRegistry } from '../validation/registry'\nimport { FORMS_SLUG } from './forms'\n\nexport const FORM_SUBMISSIONS_SLUG = 'form-submissions'\n\ntype BuildSubmissionsCollectionArgs = {\n\tregistry: FieldTypeRegistry\n\truleRegistry: ValidationRuleRegistry\n\tconsentRegistry: ConsentSourceRegistry\n\tactionRegistry?: ActionRegistry\n\tevents?: FormEventSink\n\t/** Whether a job runner is likely present; gates the queued vs bounded-inline dispatch path. */\n\thasRunner?: boolean\n\t/** Body serialization customization forwarded to the inline action dispatch path. */\n\trichText?: RichTextBodyOption\n\t/** Upload collection slug for file fields without an explicit `relationTo`. */\n\tuploadSlug?: string\n\t/** Resolved spam config; when active, prepends the spam guard before validation. `false` disables it. */\n\tspam?: ResolvedSpamConfig | false\n\t/**\n\t * When `true`, shows the raw `values`, `descriptors`, and `consent` JSON fields in the admin UI.\n\t * Default `false` — they are fully represented by the `SubmissionAnswers` UI component.\n\t */\n\tshowRawFields?: boolean\n\toverrides?: CollectionOverrides\n}\n\nconst formIdOf = (form: unknown): number | string | undefined => {\n\tif (typeof form === 'number' || typeof form === 'string') {\n\t\treturn form\n\t}\n\tif (form && typeof form === 'object' && 'id' in form) {\n\t\tconst id = (form as { id: unknown }).id\n\t\tif (typeof id === 'number' || typeof id === 'string') {\n\t\t\treturn id\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * On a completed submission create, dispatch the form's post-submit actions and emit `submission.created`.\n * Both are side effects on an already-written row, so the whole body is wrapped: nothing it does can throw\n * past the hook (a failed action or sink must never fail the submission write). Dispatch is itself bounded\n * and non-throwing; the inline fallback is awaited so the bound caps the added latency, while the queued\n * path returns immediately. The form's `actions` are null-guarded for legacy rows created before the field\n * existed.\n */\nconst makeAfterChange =\n\t(args: {\n\t\tactionRegistry: ActionRegistry\n\t\tevents?: FormEventSink\n\t\thasRunner: boolean\n\t\trichText?: RichTextBodyOption\n\t}): CollectionAfterChangeHook =>\n\tasync ({ doc, operation, req }) => {\n\t\tif (operation !== 'create' || (doc.status != null && doc.status !== 'complete')) {\n\t\t\treturn doc\n\t\t}\n\t\tconst { payload } = req\n\t\ttry {\n\t\t\tconst formId = formIdOf(doc.form)\n\t\t\tif (formId == null) {\n\t\t\t\treturn doc\n\t\t\t}\n\t\t\tconst form = await payload\n\t\t\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, overrideAccess: true, req })\n\t\t\t\t.catch(() => null)\n\n\t\t\tawait dispatchActions({\n\t\t\t\tactions: (form?.actions ?? null) as ActionInstance[] | null,\n\t\t\t\tformId,\n\t\t\t\tsubmissionId: doc.id as number | string,\n\t\t\t\tregistry: args.actionRegistry,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t\thasRunner: args.hasRunner,\n\t\t\t\trichText: args.richText,\n\t\t\t})\n\n\t\t\ttry {\n\t\t\t\tawait resolveEventSink(args.events).emit({\n\t\t\t\t\ttype: 'submission.created',\n\t\t\t\t\tformId: String(formId),\n\t\t\t\t\tsubmissionId: String(doc.id),\n\t\t\t\t\tat: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tpayload.logger?.error(\n\t\t\t\t\t`@10x-media/form-builder: submission.created sink threw: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tpayload.logger?.error(\n\t\t\t\t`@10x-media/form-builder: afterChange dispatch failed for submission ${String(doc.id)}: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`\n\t\t\t)\n\t\t}\n\t\treturn doc\n\t}\n\nexport const buildSubmissionsCollection = ({\n\tregistry,\n\truleRegistry,\n\tconsentRegistry,\n\tactionRegistry = new Map(),\n\tevents,\n\thasRunner = false,\n\trichText,\n\tuploadSlug,\n\tspam,\n\tshowRawFields = false,\n\toverrides,\n}: BuildSubmissionsCollectionArgs): CollectionConfig => {\n\tconst defaultFields: Field[] = [\n\t\t{ name: 'form', type: 'relationship', relationTo: FORMS_SLUG, required: true },\n\t\t{\n\t\t\tname: 'status',\n\t\t\ttype: 'select',\n\t\t\tdefaultValue: 'complete',\n\t\t\toptions: [\n\t\t\t\t{ label: labelForKey(keys.statusComplete), value: 'complete' },\n\t\t\t\t{ label: labelForKey(keys.statusPartial), value: 'partial' },\n\t\t\t],\n\t\t\t// Defense-in-depth at the REST layer: anonymous clients cannot set status via the API.\n\t\t\t// The validateSubmission hook also forces 'complete' server-side, so this covers both paths.\n\t\t\taccess: { create: isLoggedIn, update: isLoggedIn },\n\t\t},\n\t\t// answers UI appears first so it is the dominant view when opening a submission document.\n\t\t{\n\t\t\tname: 'answers',\n\t\t\ttype: 'ui',\n\t\t\tadmin: {\n\t\t\t\tcomponents: { Field: '@10x-media/form-builder/rsc#SubmissionAnswers' },\n\t\t\t},\n\t\t},\n\t\t{ name: 'locale', type: 'text' },\n\t\t{ name: 'values', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'descriptors', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'consent', type: 'json', admin: { hidden: !showRawFields } },\n\t\t{ name: 'meta', type: 'json' },\n\t]\n\n\treturn {\n\t\t...(overrides ?? {}),\n\t\tslug: FORM_SUBMISSIONS_SLUG,\n\t\tlabels: {\n\t\t\tsingular: labelForKey(keys.collectionSubmissionSingular),\n\t\t\tplural: labelForKey(keys.collectionSubmissionPlural),\n\t\t\t...(overrides?.labels ?? {}),\n\t\t},\n\t\tadmin: { group: 'Forms', ...(overrides?.admin ?? {}) },\n\t\taccess: {\n\t\t\tcreate: () => true,\n\t\t\tread: isLoggedIn,\n\t\t\tupdate: () => false,\n\t\t\t...(overrides?.access ?? {}),\n\t\t},\n\t\thooks: {\n\t\t\t...(overrides?.hooks ?? {}),\n\t\t\t// Spam guard + validateSubmission must remain first: they enforce the security invariant\n\t\t\t// that anonymous callers cannot bypass post-submit actions or supply a forged status.\n\t\t\t// Consumer beforeValidate hooks are appended after so they run on already-validated data.\n\t\t\tbeforeValidate: [\n\t\t\t\t...(spam ? [buildSpamGuard(spam)] : []),\n\t\t\t\tvalidateSubmission({ registry, ruleRegistry, consentRegistry, uploadSlug }),\n\t\t\t\t...(overrides?.hooks?.beforeValidate ?? []),\n\t\t\t],\n\t\t\tafterChange: [\n\t\t\t\tmakeAfterChange({ actionRegistry, events, hasRunner, richText }),\n\t\t\t\t...(overrides?.hooks?.afterChange ?? []),\n\t\t\t],\n\t\t},\n\t\tfields: overrides?.fields ? overrides.fields({ defaultFields }) : defaultFields,\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { keys } from "../translations/keys.js";
|
|
2
2
|
import { labelFor, labelForKey } from "../translations/server.js";
|
|
3
3
|
import { isLoggedIn } from "../plugin/access.js";
|
|
4
|
+
import { normalizeCalc } from "../calc/normalizeCalc.js";
|
|
4
5
|
import { buildActionBlocks } from "../actions/buildActionBlocks.js";
|
|
5
6
|
import { resolveFormResultsRequest } from "../aggregation/resolveResultsRequest.js";
|
|
6
|
-
import { normalizeCalc } from "../calc/normalizeCalc.js";
|
|
7
7
|
import { buildConditionTypeMap } from "../conditions/conditionType.js";
|
|
8
8
|
import { normalizeFormConditions } from "../conditions/normalizeConditions.js";
|
|
9
9
|
import { buildFieldBlocks } from "../fields/buildFieldBlocks.js";
|
|
@@ -58,6 +58,64 @@ const buildFormsCollection = ({ overrides, registry, ruleRegistry, consentRegist
|
|
|
58
58
|
}
|
|
59
59
|
return data;
|
|
60
60
|
};
|
|
61
|
+
const fieldsField = {
|
|
62
|
+
name: "fields",
|
|
63
|
+
type: "blocks",
|
|
64
|
+
blocks: buildFieldBlocks(registry, ruleRegistry, consentRegistry)
|
|
65
|
+
};
|
|
66
|
+
const flowField = {
|
|
67
|
+
name: "flow",
|
|
68
|
+
type: "json",
|
|
69
|
+
validate: validateFlow,
|
|
70
|
+
admin: { components: { Field: {
|
|
71
|
+
path: FLOW_BUILDER_REF,
|
|
72
|
+
clientProps: { conditionTypes }
|
|
73
|
+
} } },
|
|
74
|
+
typescriptSchema: [() => ({
|
|
75
|
+
type: "object",
|
|
76
|
+
required: ["steps"],
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
properties: { steps: {
|
|
79
|
+
type: "array",
|
|
80
|
+
items: {
|
|
81
|
+
type: "object",
|
|
82
|
+
required: ["id"],
|
|
83
|
+
additionalProperties: true,
|
|
84
|
+
properties: {
|
|
85
|
+
id: { type: "string" },
|
|
86
|
+
title: { type: "string" },
|
|
87
|
+
fields: {
|
|
88
|
+
type: "array",
|
|
89
|
+
items: { type: "string" }
|
|
90
|
+
},
|
|
91
|
+
next: { type: "string" },
|
|
92
|
+
transitions: {
|
|
93
|
+
type: "array",
|
|
94
|
+
items: {
|
|
95
|
+
type: "object",
|
|
96
|
+
required: ["to"],
|
|
97
|
+
additionalProperties: true,
|
|
98
|
+
properties: {
|
|
99
|
+
to: { type: "string" },
|
|
100
|
+
when: {
|
|
101
|
+
type: "object",
|
|
102
|
+
additionalProperties: true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} }
|
|
110
|
+
})]
|
|
111
|
+
};
|
|
112
|
+
const actionsField = {
|
|
113
|
+
name: "actions",
|
|
114
|
+
type: "blocks",
|
|
115
|
+
blocks: buildActionBlocks(actionRegistry),
|
|
116
|
+
label: labelForKey(keys.configActions),
|
|
117
|
+
access: { read: isLoggedIn }
|
|
118
|
+
};
|
|
61
119
|
const defaultFields = [
|
|
62
120
|
{
|
|
63
121
|
name: "title",
|
|
@@ -66,62 +124,21 @@ const buildFormsCollection = ({ overrides, registry, ruleRegistry, consentRegist
|
|
|
66
124
|
label: labelForKey(keys.fieldTitle)
|
|
67
125
|
},
|
|
68
126
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
additionalProperties: false,
|
|
85
|
-
properties: { steps: {
|
|
86
|
-
type: "array",
|
|
87
|
-
items: {
|
|
88
|
-
type: "object",
|
|
89
|
-
required: ["id"],
|
|
90
|
-
additionalProperties: true,
|
|
91
|
-
properties: {
|
|
92
|
-
id: { type: "string" },
|
|
93
|
-
title: { type: "string" },
|
|
94
|
-
fields: {
|
|
95
|
-
type: "array",
|
|
96
|
-
items: { type: "string" }
|
|
97
|
-
},
|
|
98
|
-
next: { type: "string" },
|
|
99
|
-
transitions: {
|
|
100
|
-
type: "array",
|
|
101
|
-
items: {
|
|
102
|
-
type: "object",
|
|
103
|
-
required: ["to"],
|
|
104
|
-
additionalProperties: true,
|
|
105
|
-
properties: {
|
|
106
|
-
to: { type: "string" },
|
|
107
|
-
when: {
|
|
108
|
-
type: "object",
|
|
109
|
-
additionalProperties: true
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
} }
|
|
117
|
-
})]
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
name: "actions",
|
|
121
|
-
type: "blocks",
|
|
122
|
-
blocks: buildActionBlocks(actionRegistry),
|
|
123
|
-
label: labelForKey(keys.configActions),
|
|
124
|
-
access: { read: isLoggedIn }
|
|
127
|
+
type: "tabs",
|
|
128
|
+
tabs: [
|
|
129
|
+
{
|
|
130
|
+
label: labelForKey(keys.tabFields),
|
|
131
|
+
fields: [fieldsField]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
label: labelForKey(keys.tabFlow),
|
|
135
|
+
fields: [flowField]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: labelForKey(keys.tabActions),
|
|
139
|
+
fields: [actionsField]
|
|
140
|
+
}
|
|
141
|
+
]
|
|
125
142
|
},
|
|
126
143
|
{
|
|
127
144
|
name: "defaultPresentation",
|