Skip to content

feat(app-shell): Studio's report and widget dataset pickers offer New dataset, a draft dataset in the package - #27575

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-csqe9s
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-csqe9s

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

维护者速读

  • 改了什么:Studio 里「新建报表」对话框的数据集选择器和看板组件检查器的数据集选择器下面,各加了一个「New dataset」按钮。对话框让作者填名称、选对象(包里只有一个对象时自动选中)、可选勾选分组字段和求和的数字字段,然后把一个 dataset 草稿存进当前包(走所有 Studio 条目共用的草稿门),选择器重新读目录并选中它。Studio 里这两个选择器改读叠加草稿的目录,本包的数据集排在最前;只要还没有数据集读本包的对象,就显示一行说明,指向这个按钮。
  • 为什么改:Studio cannot report or chart on the package's own objects: the report and dashboard-widget dataset pickers list only pre-existing sys_/showcase_ datasets, and Studio offers no way to create a dataset #27518:作者刚建好包里的对象,在 Studio 里却没法做报表或图表。两个选择器只列已存在的数据集(新装环境里只有 sys_/showcase_ 的),Studio 里也没有新建数据集的入口。按 triage 方向:复用现有的 dataset 元数据类型和它的草稿/发布门,不另起报表数据源,不自动派生数据集(ADR-0084)。
  • 风险与回滚:只动 app-shell 的 Studio 一侧和 studio 文案。Setup 的编辑器和 Create view 对话框不在 Studio 上下文里,仍读已发布目录、不出现入口。没有新的可编写键,packages/spec 未动。已知限制:数据集在发布前是草稿,Studio 画布按名查询草稿数据集会 404,发布后正常(见 Acceptance notes)。回滚 = revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/studio-design/datasetCreate.tsx(buildDatasetSkeleton、NewDatasetDialog、DatasetCreateEntry),然后是 interfaceCreate.tsx 的 ReportCreateFields 和 DashboardWidgetInspector.tsx 里接入的那几行。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/datasetCreate.test.tsx,应见 11 个用例全部通过(写出的内容过 DatasetSchema、入口、空态条件、创建后选中,以及 Studio 外没有入口)。
  • 需要你做的:无,席位落地。

Fixes #27518
Clause-②: no
Metadata-protocol: none

Under the claim 6495604398. Studio's New report dialog and the dashboard widget inspector now offer New dataset beside their dataset pickers. It saves a dataset draft in the current package through the draft door, and the picker selects it. While no dataset reads one of the package's objects, the picker says so and points at the button.

Branch claude/project-thread-csqe9s (the card thread's harness-pinned branch), cut from origin/main at aef631b80.

What changes

The entry (ui/packages/app-shell/src/views/studio-design/datasetCreate.tsx, new)

  • StudioDatasetAuthoringContext: the Interfaces pillar provides { packageId, onDraftSaved }. A dataset picker that finds it is in the Studio. Outside the Studio there is no context, so Setup's editors are unchanged.
  • DatasetCreateEntry: the line and the button a picker renders.
    • The line shows while no dataset in the picker's catalog has an object among this package's non-system objects, whatever else the catalog holds (the card's empty-state condition). It reads the draft-overlaid object catalog (useObjectCatalog), so a draft-only object counts.
    • The dialog opens on the package's object when the package has exactly one.
    • A create calls the catalog's new reload and then hands the name to the picker.
  • NewDatasetDialog: the shared CreateItemDialog (name and derived, editable identifier), an ObjectPicker, and two checklists read from the draft-overlaid object (useObjectFields):
    • Group by: select, radio, boolean, toggle, lookup and master-detail fields. A text field holds a value per record and a date needs a granularity, so both are left to the dataset designer.
    • Totals: number, currency and percent fields, one sum each.
    • Platform bookkeeping columns (system and hidden) are left out, as the Data rail leaves them out (isStudioHiddenSystemField).
    • An object the server does not serve is refused in the dialog. A name any dataset already holds, published or draft, is refused before anything is written (list('dataset') + listDrafts({ type: 'dataset' })), and the save sends ifNoneMatch: '*' for the race.
    • The save is client.save('dataset', name, body, { mode: 'draft', packageId, ifNoneMatch: '*', surface: 'studio' }), then the pillar's onDraftSaved (the pending-changes count).
  • buildDatasetSkeleton: the spec's dataset create seed (getMetadataCreateSeed('dataset'), its label-less count measure included), the author's name, label and object, one dimension per group-by field (type inferred as the dataset designer infers it, fieldTypeToDimensionType) and one sum measure per total. A total whose name the seed already holds gets _sum, because measure names are unique. A field's label is written only when it differs from its name.
  • ⛔ Nothing is derived without the author: no dataset exists until the author saves one (ADR-0084). The body is an ordinary dataset document, so Setup › Datasets edits it afterwards.

The pickers

  • ReportCreateFields (interfaceCreate.tsx) takes the pillar's authoring. It reads the draft-overlaid catalog, lists the package's datasets first, and renders the entry under the picker. With an empty catalog it renders the entry alone, which replaces the old "There is no dataset to report on yet" line. A create binds { dataset: name, measure: '' }.
  • DashboardWidgetInspector: in the Studio, the draft-overlaid catalog, the package's datasets first, and the entry under the picker when the dashboard is writable. A create patches the widget's dataset.
  • ReportDefaultInspector: in the Studio it reads the draft-overlaid catalog too, so a report bound to a dataset that is still a draft offers its measures and dimensions in the Properties panel.
  • useDatasetCatalog(override?, { previewDrafts }) reads through client.withPreviewDrafts(true) when asked, and returns previewDrafts and reload. useDatasetSemantics hydrates a dataset through the same overlay. Entries carry packageId and draft from _packageId / _draft.
  • dataset-picker-options.ts gains packageDatasetsFirst and readsAnyObject.
  • InterfacesPillar (StudioDesignSurface.tsx) provides the context around its whole surface and passes it to ReportCreateFields.

Copy, docs, changeset

  • studio namespace (en, zh): engine.studio.interfaces.newDataset.* added, engine.studio.interfaces.create.noDatasets removed (its only reader was the branch this replaces). The dialog reuses create.idLabel, createDraft, creating and loading.
  • content/docs/data-modeling/analytics.mdx gains "Creating one in Studio". The app-shell README names the context and the entry.
  • .changeset/27518-studio-new-dataset.md: @object-ui/app-shell and @object-ui/i18n patch, with Clause-②: no and Metadata-protocol: none copied from the claim.

No packages/spec change and no new authorable key.

Tests

  • New: ui/packages/app-shell/src/views/studio-design/datasetCreate.test.tsx, 11 cases.
    • What the dialog writes: the full body for picked fields, parsed by DatasetSchema; the count-only body with nothing picked; and a CONTROL showing that the spec refuses a dataset with no object, which is why the dialog asks for one.
    • The picker entry: in the New report dialog it reads the draft-overlaid catalog (withPreviewDrafts(true)) and renders under the picker.
    • The empty-state condition: the line shows while only a platform dataset (sys_login_metrics over sys_user) exists; it is gone once a dataset reads the package's object, and that dataset is listed first; with no dataset at all, the entry stands in place of the picker.
    • The post-create selection: the save arguments (draft mode, this package, ifNoneMatch: '*'), onDraftSaved, the binding {"dataset":"work_orders","measure":""}, the picker's trigger text, the new dataset's count measure offered at once, and the line gone.
    • A taken name is refused, and nothing is saved.
    • The widget inspector: with the context, the line shows and a create patches the widget's dataset; without it there is no entry and the catalog is the published one.
  • Rewritten pin: interfaceCreate.test.tsx "says so when there is no dataset to report on" becomes "offers to create one when there is no dataset to report on": the line and the button in place of the picker.
  • Moved pins (rig only): the two ReportCreateFields suites pass authoring and answer withPreviewDrafts. Eight Interfaces pillar suites gain withPreviewDrafts on their metadata double (InterfacesPillar.heldWidget-11910, .componentListServedTitles-12688, .saveErrorClears-11950, .showMeFocus-26730, StudioDesignSurface.interfacesCreate-11823, .heldWidgetCreate-26845, .heldWidgetLeave-26705, .heldWidgetPublish-11910). No assertion in them changed.

Acceptance notes

On a fresh examples/app-showcase boot (objectstack dev --seed-admin --fresh) with the console dev server from this branch, signed in as the seeded admin, all through the UI except the seven sample records (posted to /api/v1/data/fieldsvc_work_order after publish):

  1. New package com.example.fieldsvc, object Work Order with a Status picklist and an Estimated Hours number, app Field Service.
  2. Interfaces › Add to navigation › Report. The dataset picker lists only sys_ and showcase_ datasets; the line and New dataset sit under it.
  3. New dataset "Work orders" over Work Order (preselected), grouped by Status, totalling Estimated Hours. The report dialog selects it and offers its measures. Saved as the report "Work orders by status".
  4. Add to navigation › Dashboard "Operations" › Add widget › Bar chart. The widget's dataset picker lists "Work orders" first and shows no line (a dataset now reads the package's object). New dataset "Work order hours" from the inspector binds the widget; Status and Estimated Hours picked.
  5. Publish (6 drafts). The report shows New 4.5, Scheduled 7, Done 6.5, Total 18, and the bar chart draws the same three bars.
New report dialog: the line and New dataset The picker before: only sys_ and showcase_ datasets
New dataset dialog The new dataset selected, its measures offered
Widget picker: the package's dataset first New dataset from the widget inspector
The widget bound to the new dataset After publish: the report
After publish: the bar chart

The screenshots are committed at 3d8ae9d5 and removed by the next commit, so none of them is in this PR's diff.

Scope calls, stated so they can be overruled:

  • The canvas before publish. The report and widget canvases query a dataset by name, and queryDataset (@object-ui/data-objectstack) asks for draft definitions only when the page URL carries ?preview=draft, which the Studio canvas does not. So until the package publishes, the canvas of a report or widget bound to a draft-only dataset shows the dataset-not-found error ("Dataset … is not defined in this environment"). That is already the case for any draft-only dataset, however it was created. The Properties panel and the pickers resolve the draft, and after Publish the canvas draws. Changing how the canvas previews drafts is a data-adapter question outside this card.
  • No New dataset in the Interfaces "+" menu. A dataset is not a navigation entry, so the entry sits where a dataset is chosen.
  • Text and date fields are not offered to group by. Joins, filters, date dimensions and derived measures are left to Setup › Datasets.

Verification

Local readings, on a box other sessions share, so durations are left out. "HEAD" is 27430e00.

  • New and moved tests, first pass (before the pillar doubles were found): pnpm --dir ui exec vitest run over datasetCreate.test.tsx, both ReportCreateFields suites and views/metadata-admin/inspectors/: 207 files, 2840 passed, 1 skipped, exit 0.
  • Studio, metadata-admin, report views, i18n: pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design packages/app-shell/src/views/metadata-admin plus CreateViewDialog.chartBinding-11576, ReportConfigPanel.saveOutcome-11583, ReportConfigPanel.panelTitle, ReportView.saveRefusal-11583, pageRouteLoadsNoStudioChunks-12187 and packages/i18n: 1004 files, 1003 passed, 1 failed. The failure was InterfacesPillar.showMeFocus-26730 (2 cases: its double had no withPreviewDrafts), fixed in HEAD. Then that file and the three other suites outside those directories that render the Studio surface (AiChatPage.appliedCardDoors-12894, AppHeader.studioVariant-11863, DraftChangesPanel.entryNote-26730): 4 files, 17 passed, exit 0.
  • ⚠ The whole app-shell suite was not completed locally. A full vitest run packages/app-shell was stopped at 30 minutes after it had reported seven of the eight pillar suites fixed above; CI's run is the reading for the rest.
  • Type-check: pnpm --filter @object-ui/app-shell run type-check (tsc --noEmit and tsc -p tsconfig.test.json) exit 0, and @object-ui/i18n type-check exit 0, both on c307d688. HEAD adds only the one-line double in showMeFocus-26730, the same line the other seven suites typed with.
  • Lint: eslint over the 20 changed .ts/.tsx files: 0 errors, 42 warnings (react-refresh/only-export-components, react-hooks/set-state-in-effect), the kinds these files already carry.
  • Build: the full pnpm build (114 of 114 tasks) on the first commit (add1bafd).
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 66 commands at c307d688. Each was run with its exit code recorded before any pipe, and all 66 exited 0. Re-derived at HEAD, the list is identical. --ran: "66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED", 0 UNRUN. Per the tool, this is not a full account of CI: the artifact-roster and wide-population families, the Build Docs and Plan Test Core shards jobs, and the type-check lanes are outside it.
  • node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: 5 commit messages, no card relation, no model identifier.
  • Main: origin/main is 9 commits ahead at 556f3230; git merge-tree reports no conflict, so main is not merged in.
  • Acceptance: run as described above, against the first commit's source code. No source file changed after it: the later commits add tests, docs, the changeset and the evidence pair.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L6bhgVyybmRQMp8wgFRv8v

…s (WIP)

Studio's report dialog and dashboard widget inspector now read the
draft-overlaid dataset catalog with the package's own datasets first, say
when no dataset reads the package's objects, and offer a New dataset dialog
that saves a dataset draft in the package and binds it.

Claude-Session: https://claude.ai/code/session_01L6bhgVyybmRQMp8wgFRv8v
Co-authored-by: Claude <noreply@anthropic.com>
…ocs and changeset (#27518)

- datasetCreate.test.tsx: what the dialog writes (judged by DatasetSchema), the
  picker entry, the empty-state condition (no dataset reads one of the
  package's objects) and the post-create selection, for the New report dialog
  and the widget inspector; outside the Studio there is no entry.
- The existing ReportCreateFields suites pass the authoring prop, and the
  Interfaces pillar doubles answer withPreviewDrafts.
- analytics.mdx gains "Creating one in Studio"; the app-shell README names the
  context and the entry.

Claude-Session: https://claude.ai/code/session_01L6bhgVyybmRQMp8wgFRv8v
Co-authored-by: Claude <noreply@anthropic.com>
The pull request body links them at the previous commit.

Claude-Session: https://claude.ai/code/session_01L6bhgVyybmRQMp8wgFRv8v
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

席位核对 · ACCEPT · 2026-10-29T13:54Z

Head-sha: 27430e0063483dc65f3805a3c4d9b72dc6817ed1 · card #27518 · claim 6495604398 · dev report 6498192892.

  • 形态: draft → main, Fixes #27518 only, 23 files (+964/−32): datasetCreate.tsx and its test, useDatasetCatalog.ts, the two inspectors and dataset-picker-options.ts, interfaceCreate.tsx, StudioDesignSurface.tsx, eight test doubles gaining withPreviewDrafts, studio i18n en/zh, app-shell/README.md, content/docs/data-modeling/analytics.mdx, one patch changeset (app-shell, i18n). check-governed-merges --pr 27575: NOT governed, 996 lines ≤ 3000.
  • 前提实测: seat read of the diff against origin/main: New dataset saves an ordinary dataset draft through the existing draft door (spec create seed with its count measure, the picked object, one dimension per group-by field, one sum per total), refuses a name any published or draft dataset holds before writing, then reloads the catalog and binds the name. The Studio pickers ask useDatasetCatalog(…, { previewDrafts: true }), and semantics hydration reads the same world; without the option the catalog is the published list as before. No second data source, no implicit datasets, no new authorable key, packages/spec untouched.
  • 门禁 check-runs (pinned to head): 51 success, 8 skipped, 0 failed, 0 pending; mergeable_state: clean. New tests 11; the one local suite red (a test double lacking withPreviewDrafts) was fixed and re-run green; full app-shell left to CI, green here. App-showcase acceptance with screenshots in the PR body.
  • 散文面逐句核: analytics.mdx 「Creating one in Studio」 and the app-shell README section match the diff sentence by sentence (button on both pickers, name/object/group-by/total, draft saved and selected, count always, no implicit datasets, empty state, package's datasets first, Setup keeps the published catalog). The doc's "a report or a chart can bind it at once" is true of binding; the canvas preview before publish is the finding below.
  • 接受的偏差: screenshots hosted by an add-then-remove evidence commit pair on the branch (net diff has no images; squash keeps them out of main); engine.studio.interfaces.create.noDatasets removed (its only reader was the replaced branch); docs added per ui/AGENTS.md ✨ Set up Copilot instructions #2.
  • 范围外去向: Studio canvas queries a draft dataset without previewDrafts and blames the environment → filed Studio report/widget canvas says a draft dataset "is not defined in this environment" until the package publishes #27585 (finding, triage grades). Setup's useDatasetFields not reading draft objects → noted on Studio report/widget canvas says a draft dataset "is not defined in this environment" until the package publishes #27585 (code-read). Skills-docs: not owed (Metadata-protocol: none).
  • 落地条款: ordinary queue landing; pr_ready + automerge_enable. This seat's landing op is refused by its session's permission classifier (Studio OWD panel: saving "Controlled by parent" on a detail whose master_detail is not yet required answers 409 DESTRUCTIVE_CHANGE with no way forward #27458 6497508946), so the maintainer readies and merges.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 19:33
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 19:33
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit e35db83 Oct 29, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-csqe9s branch October 29, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant