Skip to content

Commit 41b1333

Browse files
fix(spec): a view container's form is its default form — never collapsed into a named form, and no named form is promoted (#21535)
Fixes #21500 Clause-②: no ## What this changes `ViewSchema` declares `form` a view container's **default form** and `formViews` **additional named form views**. `expandViewContainerWithDiagnostics` (`packages/spec/src/ui/view.zod.ts`), the one expansion every view registrar shares (ObjectQL boot loop, metadata artifact/HMR loader, runtime expansion in `metadata-protocol`), now serves exactly that. This executes triage's ruling of record on the card (Q1 A, Q2 A): - **Form family.** The `{ type, label, columns }` signature de-duplication (`formSigSeen`) is gone. The container's `form` is always served as its own item, `OBJECT.form`, flagged `isDefault`, and it is the only form item flagged. A named form whose body equals `form` stays its own named item. - **No `form`, no default form.** The first-named promotion (`isDefault` stamped on `out[formStart]`) is gone. A container with no `form` serves every named form by name, with no `isDefault` key, the same spelling the list family uses for "not default". - **List family, folded in (same function, same signature helper).** The default `list` used to collapse into any named list sharing its `{ type, label, columns }`, so a named list with a different body (a filter, a sort) took the default's place and the default list's own body was not served. It now collapses only into a named list that restates its whole body, the view's own `name` aside, which is the "default == `listViews.all`" pattern the collapse exists for. A named list that differs in anything else is its own view, and `list` is served beside it as the default. The example containers whose create/edit form lived in `formViews` move it into `form`. The public Web-to-Lead and contact-us forms stay named. ⛔ No serve-path, `metadata-protocol`, `objectql` or console change. ## In-repo containers whose served form family changed Measured by running the pure function over all 11 in-repo `defineView` containers (`examples/**`, `packages/qa/downstream-contract`; `packages/apps/**` holds none), before (spec `dist` built at `88fb5e85a0`) and after (this branch). `*` marks the `isDefault` item. The list family is unchanged in all 11. | Container | Before | After | Intent, read from the container's own text | |---|---|---|---| | `crm_activity` (app-crm) | `default*` | `form*` | `formViews.default`, the only form, keyed `default`. Moved into `form`. | | `crm_lead` (app-crm) | `web_to_lead*`, `default` | `web_to_lead`, `form*` | `default` is the full lead form. `web_to_lead`'s docblock: "PUBLIC / ANONYMOUS — Web-to-Lead", hosted at `/forms/contact-us`. `default` moved into `form`; `web_to_lead` stays named. | | `crm_opportunity` (app-crm) | `default*` | `form*` | As `crm_activity`. | | `showcase_business_unit` | `edit*` | `form*` | Its comment: "`edit`, not `default`: the main `list` implicitly claims OBJECT.default ... so a `default` form key collides". Intended as the default; moved. | | `showcase_project` | `edit*` | `form*` | Same comment; moved. | | `showcase_task` | `edit*`, `tabbed`, `wizard`, `split`, `quick` | `tabbed`, `wizard`, `split`, `quick`, `form*` | Same comment ("Keyed `edit`, NOT `default`"); moved. `showcase_log_time`, `showcase_new_task` and the Overview page's Create Task button now target `showcase_task.form`. | | `showcase_inquiry` | `contact*` | `contact` | `contact` is the PUBLIC anonymous web-to-lead form (docblock, `sharing.allowAnonymous`, `publicLink`). Not a create/edit form, so it stays named and the container has no default form. No source change. | | `showcase_contact` | `create*` | `create`, `form*` | Already authors `form` (the documented create-vs-edit pattern in `content/docs/ui/create-vs-edit-form.mdx`). No source change; now served as documented. | For every migrated container the default form's body is the body that was `isDefault` before, so create/edit render the same form; `crm_lead` and `showcase_contact` change to the form their authors declared. Companion edits the move needs: `examples/app-crm/test/i18n-sections.test.ts` walks the default `form` as well as `formViews` (otherwise moving the forms silently dropped 8 of its 9 sections from coverage, and its non-vacuity guard would have read 1); the showcase coverage collector counts the default `form`'s type, symmetric with the list collector; the app-crm translation comments and two platform-checklist strings name the new identities. `content/docs/ui/views.mdx` taught a container whose only form was `formViews.edit`, which after this change declares no default form; it now teaches `form` as the default and `formViews` as named forms served by name. `create-vs-edit-form.mdx` already describes what is served. ## The list family and the `default_2` reading - **Falsifying case, before:** `list: { type: grid, label: 'All Leads', columns: [name, company] }` beside `listViews.mine` with the same three keys plus a `filter`. The default list was collapsed into `mine`: one item, `crm_lead.mine`, flagged default, and the default list (no filter) was not served. **After:** `crm_lead.mine` (named, not default) and `crm_lead.default` (default, no filter). Pinned. - **Identical restatement:** app-crm's `list` restates `listViews.all` key for key in a different key order; it still collapses into `all` in all three app-crm containers (scan above). Pinned, including the case where only the list's own `name` differs. - **`default` renamed `default_2` on app-crm:** not reproduced at `88fb5e85a0`. With the pure function and with the composed app-crm stack, `crm_lead`'s `formViews.default` was served as `crm_lead.default` with zero collisions, because the default `list` collapses into `listViews.all` and leaves `crm_lead.default` free. The rename mechanism itself (a nameless default `list` claims `OBJECT.default`, a later `formViews.default` is renamed `OBJECT.default_2` and reported as a collision) is a different rule: the shared name space, not the signature or the default promotion. It survives this change and is already loud (rename warning, `collisions`, `os lint`). After the migration no app-crm container has a `formViews.default`. ## Measurements - **Door probe** (`packages/qa/dogfood/test/view-container-default-form.dogfood.test.ts`, the `domain:engine` seat's commits brought in unchanged): green at HEAD (3 passed): the HotCRM shape, the two-form shape and the `crm_account` control. Its expectations are untouched. - **Real HotCRM container** (hotcrm `0b2a9d4`, `src/sales/views/lead.view.ts`, 7 named forms), through this branch's spec `dist`: `crm_lead.form` is served with its six sections and is the only `isDefault`; the seven named forms are served by name, none default. - **Overlay re-measure (unscoped kernel, `@objectstack/verify` `bootStack`, real HTTP):** packaged `crm_contact` = `form` + `formViews.detail_form`. `PUT /meta/view/crm_contact` (200) with the HotCRM shape (`form` + `detail_form` + `web_to_lead`) now serves `crm_contact.detail_form` (order 0, not default), `crm_contact.web_to_lead` (1, not default), `crm_contact.form` (2, **default**, the overlay's own sections). One default form; before this change the same overlay served two. A second overlay, of `crm_account`, authoring named forms and no `form`, serves `crm_account.detail_form`, the packaged `crm_account.form` (still the one default) and `crm_account.web_to_lead`. One default. NOT MEASURED on `env_local`: the session's permission policy refused setting up the CLI standalone-stack driver. - **What the console does with a container that has no `form`:** objectui's merge rule at the `.objectui-sha` pin (`applyViewItem`: `if (view.isDefault || !bucket.form) bucket.form = entry;`), run verbatim on this branch's rows, still makes `showcase_inquiry.contact` the console's `.form`: the first form item, with no `isDefault`. `crm_lead` and `showcase_contact` resolve to their `form`. Recorded in Acceptance notes. ## Ablation The `formSigSeen` de-duplication alone re-planted (`scripts/ablation-replace.mjs`, wrap mode; anchor 1 → 0, blob `5053b26a8f` → `8cc32810e7`), spec rebuilt, `scripts/ablation-dist-preflight.mjs` marker present in 6 built files: - the spec pins: 2 failed, 3 passed (the HotCRM collision shape and the named-form-equals-`form` case); - the door probe: 1 failed, 2 passed (the HotCRM shape, `crm_lead.form` missing; the two-form and control cases stay green). Restored by blob (`5053b26a8f` == HEAD, `git diff HEAD` empty, tree clean), spec rebuilt, preflight `--absent` over 228 built files: pins 5/5 and probe 3/3 green. Run at `78f36c0ebb`; the form family is byte-identical at HEAD. ## Tests updated, and why - `packages/lint/src/validate-translation-references.test.ts`, `packages/cli/src/utils/i18n-extract.ts`, `packages/objectql/src/engine-nested-plugin-view-expansion.test.ts`, `scripts/codemod/view-to-viewitem.mjs`: comments only. They stated the old `{ type, label, columns }` collapse rule; their fixtures and assertions are unchanged and green. - `examples/app-showcase/test/actions.test.ts`: the two `target` assertions follow the task form's move to `showcase_task.form`. - No test was found pinning the defect itself. The lint fixture whose default list differs from `listViews.all` only by the list's own `name` is what decided that `name` is identity, not body. ## Gates All readings below are at HEAD `3fa9e55aed`. - **Derived set:** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 132 commands; all 132 run with the exit code recorded before any pipe; `--ran`: "132 derived, 132 run, 0 NOT-MEASURED, 0 UNRUN". Two first answered exit 3, PREREQUISITE NOT MET (`check:skill-examples` reads `@objectstack/client-react`'s declarations; `check:dual-build-cjs-loads` reads 8 packages outside this diff's build closure). Those packages were built and both re-ran to exit 0. - **Release level:** `node scripts/check-changeset-no-major.mjs --base origin/main`, with this body as the `--event` payload: "This diff introduces no `major` bump." and "LEVEL AXIS: this PR declares clause-② `no`". `check-adr-0087-registration`: "this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)". `check-empty-changeset`: "1 declaring changeset(s) added". - **`@objectstack/spec`:** `build`; `check:generated` ("All 15 generated artifacts are up to date"); `typecheck` (tsc, scripts, test layer); the `local` suite, 604 files / 17844 tests passed; the three view-related `repo`-project files, 64 tests passed. No generated artifact changed. - **Consumers of the expansion** (each package's test files that read it): `metadata` 52, `metadata-protocol` 217, `objectql` 46, `rest` 76, `lint` 686, `metadata-core` 27, `cli` unit 81. All passed. - **Example apps:** `@objectstack/example-crm` typecheck, plus 45 tests (the i18n sweep now lists the 8 moved `form` sections); `@objectstack/example-showcase` typecheck, plus 394 tests. **Dogfood:** the door probe 3/3; the two showcase-booting view and public-form tests 6/6. - **Declared, not run here:** the full lint-workflow farm, `pnpm lint`, the CLI integration tier and the full dogfood suite are CI's. - **Stale tree, declared:** at HEAD the branch is 6 commits behind `origin/main` (`550f4cc2fd`). Two gate-config files changed there: the doc-authoring prose-id baseline (shrunk by the spec strings stage) and the engine-double pin list. This diff feeds neither family: no runtime string here carries a tracker id, and no engine double is added. CI judges the merge ref. ## Acceptance notes - **objectui still promotes the first named form for a container with no `form`** (measured above, `showcase_inquiry`). The spec no longer flags it; the console's `!bucket.form` fallback does. Filed as objectstack-ai/objectui#11539; no console change here. - **The published `objectstack-ui` skill** (`skills/objectstack-ui/SKILL.md` subforms example, `rules/list-views.md` container example and its eval) teaches the create/edit form under `formViews.default` / `formViews.edit`. After this change those forms are named, not default. `skills/**` is a Tier H surface, so it is left out of this PR. It is carded as #21537 (seat answer `5965506932`, option A), to ship in the same release. - **A dead read, noted:** the expansion's `form.name` branch (`typeof defaultForm.name === 'string' ? … : 'form'`) reads a key the strict `FormViewSchema` refuses (`unrecognized_keys`), so it is unreachable for schema-valid metadata. It is left as it is. - **Release:** `@objectstack/spec` `patch` (the expansion is corrected to the declared contract; no accept set changes). The example apps are private. > Seat edit, 2026-10-03T04:25Z: the acceptance notes name objectstack-ai/objectui#11539 and #21537, the cards filed for the two reported items, and add the `form.name` dead-read note from the dev report. --- _Generated by [Claude Code](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1ac7308 commit 41b1333

22 files changed

Lines changed: 585 additions & 292 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
A view container's `form` is its default form: it is never collapsed into a named form, and no named form is promoted to default
6+
7+
Clause-②: no
8+
9+
`ViewSchema` declares `form` the container's default form and `formViews` additional named forms. `expandViewContainer` / `expandViewContainerWithDiagnostics`, which every view registrar shares, now serves exactly that. Behaviour changes for authors:
10+
11+
- **A container with no `form` no longer serves its first named form as the default create/edit form.** Before, the first `formViews` entry was flagged `isDefault`, whatever it was: in the CRM example that was the anonymous Web-to-Lead form. Now no form item is flagged, and each named form is served only where it is asked for by name (a form action's `target`, `addRecord.formView`, a public `sharing.publicLink`). If you relied on the old promotion, move the intended create/edit form into `form`: `formViews: { edit: { … } }` becomes `form: { … }`, and a reference to `<object>.edit` becomes `<object>.form`.
12+
- **A named form no longer replaces `form`.** Before, `form` was dropped when any named form shared its `type`, `label` and `columns`, even with different sections, and the first named form became the default. Now `form` is always served as `<object>.form`, flagged `isDefault`, and is the only form item flagged. A named form whose body equals `form` stays its own named item.
13+
- **The default `list` collapses only into a named list that restates its whole body.** A `listViews` entry that repeats `list` key for key, the list's own `name` aside (the "default == `listViews.all`" pattern), still folds into that one named item. A named list that shares `list`'s `type`, `label` and `columns` but differs in anything else (a filter, a sort) is now its own view, and `list` is served beside it as `<object>.default`, the default list. Before, such a named list took the default's place, and the default list's own body was not served.
14+
15+
The CRM and showcase examples move their create/edit form into `form`. The showcase task's `showcase_log_time` and `showcase_new_task` actions now target `showcase_task.form`. The public Web-to-Lead and contact-us forms stay named.
16+
17+
⛔ No schema, parse, export or accept-set change.

‎content/docs/ui/views.mdx‎

Lines changed: 68 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A **View** defines how records of an Object are displayed to users. ObjectStack
88

99
## The `defineView` Container
1010

11-
Views are authored **per object** inside a `defineView({ ... })` container — the default `list`, named `listViews`, and named `formViews` all live in one document. The loader expands the container into independently addressable `<object>.<key>` view items that power the view switcher.
11+
Views are authored **per object** inside a `defineView({ ... })` container — the default `list`, named `listViews`, the default `form`, and named `formViews` all live in one document. The loader expands the container into independently addressable `<object>.<key>` view items that power the view switcher.
1212

1313
{/* os:check */}
1414
```typescript
@@ -43,15 +43,13 @@ export const TaskViews = defineView({
4343
},
4444
},
4545

46-
// Named form views
47-
formViews: {
48-
edit: {
49-
type: 'simple',
50-
data,
51-
sections: [
52-
{ name: 'task', label: 'Task', columns: 2, fields: ['title', 'status', 'assignee', 'due_date'] },
53-
],
54-
},
46+
// The default form — the create and edit surfaces render it
47+
form: {
48+
type: 'simple',
49+
data,
50+
sections: [
51+
{ name: 'task', label: 'Task', columns: 2, fields: ['title', 'status', 'assignee', 'due_date'] },
52+
],
5553
},
5654
});
5755
```
@@ -360,39 +358,40 @@ gantt: {
360358

361359
## Form View
362360

363-
A Form View defines how a single record is displayed for viewing or editing. Form views live under `formViews` in the same `defineView` container as the object's list views.
361+
A Form View defines how a single record is displayed for viewing or editing. It lives in the same `defineView` container as the object's list views:
362+
363+
- `form` is the object's **default form** — the create and edit surfaces render it.
364+
- `formViews` holds **additional named forms**. Each one is served only where it is asked for by name: a form action's `target` (`<object>.<key>`), `addRecord.formView`, or a public `sharing.publicLink`. A named form never becomes the default — a container with no `form` has no default form, even when it declares named ones.
364365

365366
### Basic Structure
366367

367368
```typescript
368-
formViews: {
369-
edit: {
370-
type: 'simple',
371-
data: { provider: 'object', object: 'task' },
372-
sections: [
373-
{
374-
name: 'basic_information',
375-
label: 'Basic Information',
376-
columns: 2,
377-
fields: [
378-
{ field: 'title', span: 'full' },
379-
{ field: 'status' },
380-
{ field: 'priority' },
381-
{ field: 'assignee' },
382-
{ field: 'due_date' },
383-
],
384-
},
385-
{
386-
name: 'details',
387-
label: 'Details',
388-
collapsible: true,
389-
columns: 1,
390-
fields: [
391-
{ field: 'description' },
392-
],
393-
},
394-
],
395-
},
369+
form: {
370+
type: 'simple',
371+
data: { provider: 'object', object: 'task' },
372+
sections: [
373+
{
374+
name: 'basic_information',
375+
label: 'Basic Information',
376+
columns: 2,
377+
fields: [
378+
{ field: 'title', span: 'full' },
379+
{ field: 'status' },
380+
{ field: 'priority' },
381+
{ field: 'assignee' },
382+
{ field: 'due_date' },
383+
],
384+
},
385+
{
386+
name: 'details',
387+
label: 'Details',
388+
collapsible: true,
389+
columns: 1,
390+
fields: [
391+
{ field: 'description' },
392+
],
393+
},
394+
],
396395
},
397396
```
398397

@@ -467,7 +466,7 @@ app page or action, where `features.*` stays bound and stays legal. A record *fi
467466

468467
## Complete Example
469468

470-
One container covering a default grid, a kanban saved view, and a tabbed edit form — mirroring `examples/app-showcase/src/ui/views/task.view.ts`:
469+
One container covering a default grid, a kanban saved view, and a tabbed default form — mirroring `examples/app-showcase/src/ui/views/task.view.ts`:
471470

472471
{/* os:check */}
473472
```typescript
@@ -502,37 +501,35 @@ export const TaskViews = defineView({
502501
},
503502
},
504503
505-
formViews: {
506-
edit: {
507-
type: 'tabbed',
508-
data,
509-
sections: [
510-
{
511-
name: 'details',
512-
label: 'Details',
513-
columns: 2,
514-
fields: [
515-
{ field: 'title', span: 'full', required: true },
516-
{ field: 'status' },
517-
{ field: 'priority' },
518-
{ field: 'assignee' },
519-
{ field: 'due_date' },
520-
{ field: 'description', span: 'full' },
521-
],
522-
},
523-
{
524-
name: 'system',
525-
label: 'System',
526-
collapsible: true,
527-
collapsed: true,
528-
columns: 2,
529-
fields: [
530-
{ field: 'created_at', readonly: true },
531-
{ field: 'updated_at', readonly: true },
532-
],
533-
},
534-
],
535-
},
504+
form: {
505+
type: 'tabbed',
506+
data,
507+
sections: [
508+
{
509+
name: 'details',
510+
label: 'Details',
511+
columns: 2,
512+
fields: [
513+
{ field: 'title', span: 'full', required: true },
514+
{ field: 'status' },
515+
{ field: 'priority' },
516+
{ field: 'assignee' },
517+
{ field: 'due_date' },
518+
{ field: 'description', span: 'full' },
519+
],
520+
},
521+
{
522+
name: 'system',
523+
label: 'System',
524+
collapsible: true,
525+
collapsed: true,
526+
columns: 2,
527+
fields: [
528+
{ field: 'created_at', readonly: true },
529+
{ field: 'updated_at', readonly: true },
530+
],
531+
},
532+
],
536533
},
537534
});
538535
```

‎docs/qa/platform-checklist/areas/records-forms.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@
15341534
"boot the showcase isolated; sign in as admin; open the showcase_task list view",
15351535
"for each location variant, navigate to its surface (list header / row menu / a not-done task's detail title bar / its ⋯ menu / a related-list section / the Task Detail quick-actions bar), screenshot AFTER render settles, then read the rendered action buttons from the DOM",
15361536
"record for every fixture action WHERE it rendered — building the full placement matrix (rendered locations vs declared locations)",
1537-
"dispatch one action per location with a ref-targeted click: bulk_reassign (screen-flow wizard opens), quick_view (modal opens), mark_done (script executes), log_time (form dialog opens on showcase_task.edit), open_docs (url navigation), recalc_selection from the ⋯ menu (api POST); capture each network trace",
1537+
"dispatch one action per location with a ref-targeted click: bulk_reassign (screen-flow wizard opens), quick_view (modal opens), mark_done (script executes), log_time (form dialog opens on showcase_task.form), open_docs (url navigation), recalc_selection from the ⋯ menu (api POST); capture each network trace",
15381538
"verify the state-changing dispatches server-side: mark_done flips the task's done flag (API re-read), recalc_selection's per-record branch recomputes the estimate",
15391539
"CEL visibility both sides: locate a done task and a not-done task; read the row menu and record header of each for showcase_mark_done",
15401540
"empty-locations probe, both halves: (a) in a scratch/writable package author a copy of an api action with NO locations key; reload and sweep all six surfaces (list toolbar, row menu, record header, ⋯ overflow, related-list section, quick-actions bar) recording every surface it appears on — the expected list is EMPTY — then delete the probe; (b) sweep the same six surfaces for showcase_new_task, the fixture fleet's headless `locations: []` declaration, and record that its list is empty too"
@@ -1574,7 +1574,7 @@
15741574
"negative": [
15751575
"any action rendering at a location it did not declare is a FAIL — placement is a contract, not a hint, and since objectui#3142 there is no everywhere-default left to inherit",
15761576
"showcase_recalc_selection appearing on the list toolbar is the objectui#3142 regression shape — FAIL even though clicking it would merely error",
1577-
"a dispatch that opens the wrong target (e.g. log_time opening a list view instead of the showcase_task.edit form — the #2554 build-gate class) is a FAIL of the dispatch clause, not a cosmetic note"
1577+
"a dispatch that opens the wrong target (e.g. log_time opening a list view instead of the showcase_task.form form — the #2554 build-gate class) is a FAIL of the dispatch clause, not a cosmetic note"
15781578
],
15791579
"traps": [
15801580
"hydration-race",

‎examples/app-crm/src/translations/crm.translation.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export const CrmTranslationBundle = defineTranslationBundle({
136136
all: { label: '全部商机' },
137137
pipeline: { label: '商机看板' },
138138
},
139-
// Form section heading of `ui/views/opportunity.view.ts` `default`.
139+
// Form section heading of `ui/views/opportunity.view.ts` `form`.
140140
// Declares a stable `name`, which is the only thing that makes the
141141
// heading translatable — otherwise it renders the English `label` in
142142
// every locale (#8231). Bare object word, matching this bundle's own
@@ -162,7 +162,7 @@ export const CrmTranslationBundle = defineTranslationBundle({
162162
pipeline: { label: '线索看板' },
163163
},
164164
// Form section headings of `ui/views/lead.view.ts` (public web-to-lead
165-
// `contact_us`, plus the four `default` groups). Each section declares
165+
// `contact_us`, plus the four `form` groups). Each section declares
166166
// a stable `name`, the only thing that makes the heading translatable
167167
// (#8231). `conversion` reuses this bundle's own `messages` vocabulary
168168
// for "convert" (`crm.lead.convert.success` already reads 转化).
@@ -190,7 +190,7 @@ export const CrmTranslationBundle = defineTranslationBundle({
190190
all: { label: '全部活动' },
191191
calendar: { label: '活动日历' },
192192
},
193-
// Form section headings of `ui/views/activity.view.ts` `default`. Each
193+
// Form section headings of `ui/views/activity.view.ts` `form`. Each
194194
// declares a stable `name`, the only thing that makes the heading
195195
// translatable (#8231).
196196
_sections: {

‎examples/app-crm/src/views/activity.view.ts‎

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,42 +48,41 @@ export const ActivityViews = defineView({
4848
},
4949
},
5050
},
51-
formViews: {
52-
default: {
53-
type: 'simple',
54-
sections: [
55-
{
56-
name: 'activity_details',
57-
label: 'Activity Details',
58-
columns: 2,
59-
fields: [
60-
{ field: 'subject', required: true },
61-
{ field: 'type', required: true },
62-
{ field: 'status', required: true },
63-
{ field: 'due_date' },
64-
{ field: 'duration_minutes' },
65-
],
66-
},
67-
{
68-
name: 'related_records',
69-
label: 'Related Records',
70-
columns: 2,
71-
fields: [
72-
{ field: 'contact' },
73-
{ field: 'account' },
74-
{ field: 'opportunity' },
75-
],
76-
},
77-
{
78-
name: 'notes',
79-
label: 'Notes',
80-
columns: 1,
81-
fields: [
82-
{ field: 'description' },
83-
{ field: 'outcome' },
84-
],
85-
},
86-
],
87-
},
51+
// The default form — what the create and edit surfaces render.
52+
form: {
53+
type: 'simple',
54+
sections: [
55+
{
56+
name: 'activity_details',
57+
label: 'Activity Details',
58+
columns: 2,
59+
fields: [
60+
{ field: 'subject', required: true },
61+
{ field: 'type', required: true },
62+
{ field: 'status', required: true },
63+
{ field: 'due_date' },
64+
{ field: 'duration_minutes' },
65+
],
66+
},
67+
{
68+
name: 'related_records',
69+
label: 'Related Records',
70+
columns: 2,
71+
fields: [
72+
{ field: 'contact' },
73+
{ field: 'account' },
74+
{ field: 'opportunity' },
75+
],
76+
},
77+
{
78+
name: 'notes',
79+
label: 'Notes',
80+
columns: 1,
81+
fields: [
82+
{ field: 'description' },
83+
{ field: 'outcome' },
84+
],
85+
},
86+
],
8887
},
8988
});

0 commit comments

Comments
 (0)