You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9b1dc99
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec,lint): a form's thank-you panel names the record's engine-generated fields, and the public submit answer has a declared shape (#28440)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoEJN3dFK66nJxSuhYejKnJK)_
Part of #28005
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — content/docs/ui/public-data-collection.mdx
and content/docs/ui/forms.mdx (the thank-you panel's
`{{record.field_name}}` slot, taught once the server fills the answer
and the Console fills the tokens; teaching it now would advertise a
panel that still prints the token text); no `skills/**` page names the
thank-you panel's text (`git grep -n "thank-you" -- skills/` is empty)
domain:spec#1 · os-tesla · session_01L9qibwKTredm7FjB4Z6DeX
This is the spec half of ruling B (ruling record 6545210989). The server
half (`POST /forms/SLUG/submit` fills `fields`) and the ui half
(`FormPage.tsx` fills the tokens) follow on their own cards, which the
seat files once this PR is up.
## 维护者速读
- **改了什么**:公开表单的「提交成功」面板(`thank-you` 的 `title` / `message`)可以用
`{{record.字段名}}` 引用刚提交的那条记录的字段,但只允许引擎在插入时自动生成的字段(目前就是自动编号
`autonumber`,比如案件号)。同时在 spec
里声明了公开表单提交接口的返回形状:`id`,加上面板引用到的那几个字段,别的什么都不带。
- **为什么改**:web-to-case 的访客提交后看不到自己的案件号。按裁决 B,只放引擎生成的字段,因为其它字段的值可能是 hook
从匿名访客无权读取的记录里算出来的,不能回显给匿名页面。
- **风险与回滚**:低。引用写法和 `redirect` 的 `url` 是同一个记号;写错形状(`{record.x}`、`{{
record.x }}`、`{{case_number}}`)在解析时被拒并说明正确写法;引用了非自动编号字段,在 `os validate`
/ `os build` / `os lint` 和 view、page 的发布门被拒,并点名字段和类型。现有示例、hotcrm
里没有任何面板文字带花括号(实测为零)。回滚即 revert。
- **先看**:`packages/lint/src/validate-thank-you-record-fields.ts`(判定规则,只读
`RUNTIME_OWNED_FIELD_TYPES`);`packages/spec/src/ui/view.zod.ts`(`THANK_YOU_TEXT_RE`、`checkThankYouText`、`thankYouRecordFieldNames`);`packages/spec/src/api/protocol.zod.ts`(`PublicFormSubmitResponseSchema`)。
- **两分钟验证**:`pnpm --filter @objectstack/lint exec vitest run
--maxWorkers=2 src/validate-thank-you-record-fields.test.ts`,预期 16/16
通过:自动编号字段放行,文本字段、公式字段、未声明字段、系统列各被拒并点名类型;把类型集合在测试里放宽一种类型即放行,无需改规则。
- **需要你做的**:批准三个新名字(见下文 Names for approval),其余无,席位落地。
## What changes
### 1. The slot (`@objectstack/spec`, `ui/view.zod.ts`)
The `thank-you` member's `title` and `message` stay `z.string()`: the
renderer receives the text the author wrote. Each now carries a
`.regex(THANK_YOU_TEXT_RE)`: every brace in the text must belong to a
well-formed `{{record.field_name}}` token. That is the same token the
`redirect` member's `url` takes (`SUBMIT_REDIRECT_URL_TOKEN_RE`), so
there is one grammar and no second dialect.
- **A malformed token is refused on its own terms**, in the style of
`checkSubmitRedirectUrl`. `checkThankYouText` words the refusal from the
input: it names the member (`title` or `message`), quotes the offending
text, and gives the one spelling (`{{record.case_number}}`). It also
says braces belong to that token alone, and that only an
engine-generated field may be named.
- **A regex, not a refine.** A `.regex()` projects into the published
JSON Schema as a `pattern`, so the file carries the shape too. That
means no new `x-dropped-refinements` site and no
`dropped-refinements.baseline.json` row. The two alternatives in the
pattern start on disjoint characters, so matching is linear.
- **`thankYouRecordFieldNames(submitBehavior)`** is the one reading of
which fields a panel names: `title` then `message`, each name once, in
order of first appearance, and `[]` for any other kind. It is a pure
function of the block. The lint rule judges exactly these names, and the
server half fills exactly these fields.
- The two members gain a `.describe()` that states the slot and its
limit. The union's own describe, which is projected into the governed
`skills/objectstack-ui/references/react-blocks.md`, is unchanged, so
this PR touches no governed surface.
### 2. The authoring check (`@objectstack/lint`,
`validate-thank-you-record-fields.ts`)
- **Where it lives.** I measured whether the redirect token's "declared
record fields" rule is cross-checked anywhere. It is not: no lint rule
reads `submitBehavior.url`, and the `view.zod.ts` docblock only names
the reference-integrity family as the right layer. The nearest existing
site that resolves a form's object fields is
`validate-public-form-file-fields.ts`. The new rule sits beside it, with
the same registration: gating, all three commands, the runtime publish
gate. It resolves the form's object with the same
`anonymousFormObjectName` the anonymous doors use.
- **One source.** A name is admitted only when its field's type is in
`RUNTIME_OWNED_FIELD_TYPES` (`data/field.zod.ts`, today `autonumber`).
The rule spells no field type of its own. It takes the set as a
defaulted parameter only so a test can widen it as a double: the spec
dist is externalised in lint's tests, so it cannot be module-mocked.
- **What is refused,** naming the field, its type and the reason (a
value a hook may have set from records the anonymous grant cannot read):
- a declared field of any other type (`text`, `formula`, …);
- a registry-injected column (`created_at`, `owner_id`, `id`), which is
injected, not engine-numbered;
- a name the object does not declare (the panel would print nothing).
- **Which forms.** Every form that carries a `thank-you` panel, public
or not: the bare form-view shape, the nested `form`, every `formViews`
entry, the flattened `config` of a `viewKind: 'form'` item, and a page
`object-form` component (`properties.submitBehavior`, object from
`properties.objectName`, else the page walk's binding). The slot is one
slot with one admission. A form that is internal today becomes public by
flipping its `sharing`, and that is a write this rule would otherwise
never see again.
- **Runtime publish gate:** `runtimeTypes: ['view', 'page']`. Both
snapshots carry `objects` as context, which is every input the rule
reads. `object` is not declared, for the reason the file-field rule
gives: an object write's snapshot carries no views or pages.
- A form whose object does not resolve in the stack is not judged, the
same boundary the file-field rule draws.
### 3. The answer shape (`@objectstack/spec`, `api/protocol.zod.ts`)
I measured whether any spec schema names the `POST /forms/:slug/submit`
answer. None does: `rest-route-ledger.ts` lists the route with no
`responseSchema`. `PublicFormSubmitResponseSchema` now sits next to
`CreateDataResponseSchema`:
- `id: string`;
- `fields` (optional): a map from field name to a string or number
value, holding exactly the named fields' stored values, present only
when the panel names at least one;
- **`.strict()`,** so a key beyond these two is a conformance failure
rather than a silent widening of what an anonymous caller is shown.
The server half fills it and names it as the route's `responseSchema`. ⛔
`packages/rest/src/form-routes.ts` is untouched here.
### Names for approval
The order asks for Tesla's approval of every new name. These are
proposals; renaming before merge is a mechanical edit.
| Name | Kind | Where |
|---|---|---|
| `PublicFormSubmitResponseSchema` (type `PublicFormSubmitResponse`) |
REST answer schema | `@objectstack/spec/api` |
| `fields` | key on that answer | the map of named field values |
| `thankYouRecordFieldNames` | pure helper | `@objectstack/spec/ui` |
| `thank-you-record-field` / `validateThankYouRecordFields` | lint rule
id / function | `@objectstack/lint` |
`THANK_YOU_TEXT_RE` and `checkThankYouText` are module-private.
## Tests
- **Parse**
(`packages/spec/src/ui/view-submit-thank-you-record-slot.test.ts`, 15):
- A `message` with `{{record.case_number}}` parses, and the text arrives
verbatim. Title tokens, several tokens, token-free text and an empty
panel all parse.
- Refused, each naming the offender and the one spelling, at the
member's path: `{record.x}`, `{{ record.x }}`, `{{case_number}}`, an
uppercase segment, a dotted path, and a stray `}`.
- The refusal carries no tracker id or date. The published JSON Schema
carries the shape as a `pattern`.
- `thankYouRecordFieldNames` reads title then message, each once, and
reads only well-formed tokens. It answers `[]` for other kinds and for
non-objects.
- **Answer schema**
(`packages/spec/src/api/public-form-submit-response.test.ts`, 4): it
accepts `{ id }` and `{ id, fields: { case_number } }`. It refuses
another top-level key (`unrecognized_keys`), a missing `id` and a
non-scalar value.
- **Authoring check**
(`packages/lint/src/validate-thank-you-record-fields.test.ts`, 16):
- On a fixture object, an `autonumber` token passes. `text` and
`formula` tokens are refused, and the message names the field and its
type. An undeclared name and the injected `created_at` / `id` are
refused too.
- Every form shape is judged: `form`, `formViews.*`, the `viewKind:
'form'` config, the bare form view, a public form, and a page
`object-form`. A list-shaped `fields` is read, and the object resolves
from the view.
- **One source:** widening `RUNTIME_OWNED_FIELD_TYPES` in a test double
(`+ 'formula'`) admits the formula token with no other edit. The rule
source imports the set from `@objectstack/spec/data` and spells no
`'autonumber'`.
- Registration is pinned: gating, three commands, `['view', 'page']`.
The runtime publish gate refuses a `view` write naming a text field and
admits the same write naming the autonumber field.
- **Existing pins updated:**
- The publish-gate rule rosters name the new rule in
`authoring-rule-wiring.test.ts`, `runtime-gate.test.ts` (3 sites) and
`runtime-gate.page-visibility-predicate-writes.test.ts` (2 sites).
- `type-alias-convention.pin.test.ts` pins
`PublicFormSubmitResponseSchema` as isomorphic (805 → 806, with the
reason in the count's ledger comment).
<details><summary>Ablation (one-time, restored)</summary>
Done through `node scripts/ablation-replace.mjs` on
`packages/lint/src/validate-thank-you-record-fields.ts`. The suite
imports the rule from `src/`, so no build sits between mutation and run.
The type gate is the anchor `if (type !== undefined &&
admitted.has(type)) return undefined;`, which went x1 → x0, replaced by
an unconditional `return undefined;`.
- Blob `014ba032cba7` → `e040658efab9`.
- `validate-thank-you-record-fields.test.ts`: 9 failed | 7 passed. The
text and formula refusals, every form-shape case, the page case, the
one-source widening case and the runtime gate's refusal all went red.
- Restored: blob `014ba032cba7` = HEAD, `git diff HEAD` empty.
</details>
## Acceptance notes
- **A brace in panel text is now refused unless it is the token.**
Before this PR, `title` / `message` accepted any text. Text with a brace
that is not a well-formed token (a literal `{`, `{{ x }}`) now fails to
parse. The order asked for this, mirroring the redirect refine. I
measured the corpus: no authored `thank-you` title or message in
`examples/`, the non-test `packages/` sources or `hotcrm` contains a
brace. I copied the claim's declaration lines (`yes (widening)`,
`added`) unchanged. The seat decides whether this face changes the
Clause-② arm.
- **`{{record.id}}` is refused.** `id` is not a
`RUNTIME_OWNED_FIELD_TYPES` type. The answer already carries `id` at the
top, so admitting it would be a separate decision.
- **Translations are not judged.** The i18n checks
(`validate-translation-references.ts`) read only the key address of
`…submitBehavior.title/message`, never its text, so a token in the text
breaks no translation check (measured). A translated panel could name a
field the base text does not name. The server half fills only the base
text's names, so such a token would print as written; it never leaks.
Whether to judge translated text is the ui/server cards' question.
- **An `object` write is not re-judged.** An object write's snapshot
carries no views or pages, so retyping a named field under a published
panel is caught by the CLI commands and by the form's next publish. This
is the same boundary as the file-field rule.
- Until the server and ui halves land, a panel that names a field shows
the token text verbatim. The changeset says so.
## Verification
The branch head is `eeb6adb2b`; each line names the commit it ran on
where that differs. Heavy steps ran under
`scripts/pm/os-verify-lock.sh`, and every exit code was captured before
any pipe.
- **Package suites:**
- `pnpm --filter @objectstack/spec test` on `9d1b0b28`: 774 of 775 files
passed (22847 tests, 1 todo). The one red was the isomorphic-pin count
(805 → 806), fixed in this PR, and that file then passed 3/3.
- `pnpm --filter @objectstack/lint test` on `9d1b0b28`: 183 files, 7698
passed, 5 skipped.
- **Typecheck** on `47c5f03c4`: `pnpm --filter @objectstack/lint
typecheck && pnpm --filter @objectstack/spec typecheck` exit 0.
- **Generated artefacts** on `1bc2dd670`: `pnpm --filter
@objectstack/spec check:generated --fix` rebuilt spec and regenerated
api-surface, export-origins, declaration-map, the reference docs and the
strictness ledger. It then re-checked all five green. A later
`check:generated` exit 0 is in the rerun below.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` derived 103 commands on `9d1b0b28`,
and 104 on `eeb6adb2b` (+ `check:cli-examples-parity`, from the docs
edit).
- First sweep on `9d1b0b28`: 91 exit 0.
- One real finding: `check-comment-mask-adoption` flagged a private
comment stripper in my new lint test. I removed it.
- Eleven reds read built output and had no build to read: ten exit 3
(prerequisite) and `check:dts-closure` / `check:generated` exit 1
(missing or stale dist).
- After a full `turbo run build` of `./packages/**` and `./examples/**`,
I re-ran all twelve on `b96858477`. One more real finding:
`check:docs-transcript-drift` said four CLI transcripts print
`author-time rules (56)`, and the registry now derives 57. I updated
those four pages, and the gate exits 0 on `eeb6adb2b`, as does the new
`check:cli-examples-parity`.
- The final union of all 104 on `eeb6adb2b` is in the `os-dev-report`
comment on #28005, reconciled with `dispatch-gates.mjs --ran`.
<details><summary>First sweep, 9d1b0b2 (103)</summary>
```
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 1
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 3
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 3
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 3
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 3
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 3
pnpm --filter @objectstack/spec run check:generated :: exit 1
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 3
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 3
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 1
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 3
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
</details>
<details><summary>Rerun after the full build: twelve on b968584, the
last two on eeb6adb</summary>
```
node scripts/check-comment-mask-adoption.mjs :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm check:docs-transcript-drift :: exit 1
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:cli-examples-parity :: exit 0
```
</details>
- **CI:** the full farm, and `test:repo`, are CI's; `Test Core` runs the
latter on the PR.
- **Dogfood:** not run. The title carries no `!` and the Clause-② arm is
not `(narrowing)`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibwKTredm7FjB4Z6DeX)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(spec): a form's thank-you panel can name the record's engine-generated fields, and the public submit answer has a declared shape
7
+
8
+
Clause-②: yes (widening)
9
+
Metadata-protocol: added
10
+
11
+
A public form's confirmation panel could not tell a web-to-case visitor their case number: `title` and `message` were plain text, and `POST /api/v1/forms/SLUG/submit` answers `{ id }` only.
12
+
13
+
-**The slot.** A form's `submitBehavior: { kind: 'thank-you', title, message }` may name a field of the record just submitted as `{{record.field_name}}`, the same token the `redirect` behavior's `url` takes. Any other brace is refused at parse, naming the offending text: `{record.x}`, `{{ record.x }}` and `{{case_number}}` are not the token.
14
+
-**Only engine-generated fields.** A named field must be one whose value the engine generates on insert: its type is in `RUNTIME_OWNED_FIELD_TYPES` (today `autonumber`). Any other stored value may have been set by a `beforeInsert` / `afterInsert` hook from records an anonymous visitor cannot read. `os validate`, `os build`, `os lint` and the publish gate on a `view` or `page` write refuse any other name (rule `thank-you-record-field`), naming the field and its type. This covers every form view shape and a page `object-form` component.
15
+
-**The answer shape.**`PublicFormSubmitResponseSchema` (`@objectstack/spec/api`) declares the submit answer as `id` plus an optional `fields` map holding exactly the named fields, and nothing else. `thankYouRecordFieldNames` (`@objectstack/spec/ui`) reads which fields a panel names.
16
+
17
+
The server fills `fields` and the console fills the tokens in follow-up releases. Until then, a panel that names a field shows the token text as written.
|[Automation Protocol](/docs/references/automation)| 15 | 77 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
26
26
|[Data Protocol](/docs/references/data)| 31 | 174 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
Copy file name to clipboardExpand all lines: content/docs/references/ui/component.mdx
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -653,6 +653,14 @@ Sort field and direction pair
653
653
|**group**|`string`| optional | Field group key (snake_case) whose members and presentation this section inherits, from the object's `fieldGroups`. Mutually exclusive with `fields`, and with every key the group itself declares (`name`, `label`, `description`, `visibleWhen`, and a `true``collapsible` / `collapsed`) |
654
654
|**fields**|`(string \| { field: string; type?: Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| …>; options?: object[]; reference?: string; … } \| { name: string; label?: string; description?: string; type?: string; … })[]`| optional | The section's fields, in order — each a field name, the form view's `{ field, … }` entry overriding that object field, or an inline form field `{ name, type, … }`. Omit only when `group` supplies the members |
|**title**|`string`| optional | Heading of the confirmation panel. May name a field of the submitted record as `{{record.field_name}}`, and only a field the engine generates on insert (an `autonumber` field, e.g. a case number): a hook-derived value never reaches an anonymous visitor. |
662
+
|**message**|`string`| optional | Body of the confirmation panel. May name a field of the submitted record as `{{record.field_name}}`, and only a field the engine generates on insert (an `autonumber` field, e.g. a case number): a hook-derived value never reaches an anonymous visitor. |
|**title**|`string`| optional | Heading of the confirmation panel. May name a field of the submitted record as `{{record.field_name}}`, and only a field the engine generates on insert (an `autonumber` field, e.g. a case number): a hook-derived value never reaches an anonymous visitor. |
500
+
|**message**|`string`| optional | Body of the confirmation panel. May name a field of the submitted record as `{{record.field_name}}`, and only a field the engine generates on insert (an `autonumber` field, e.g. a case number): a hook-derived value never reaches an anonymous visitor. |
0 commit comments