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 66b17a3
Browse filesBrowse the repository at this point in the historyBrowse files
feat(studio): the Delete package dialog states what goes (objects with row counts, items by type, permanent drop) (#31193)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XEaDWcgFZCJ157i8WCtYTxk)_
Part of #31025 — this lands Expected 1 (the Delete package dialog states
what goes). Expected 2, the `packageRemoval` chat confirm card, stays on
the card: its payload is produced only in the cloud repository, which
this lane cannot read, and the seat ruled the shape is not to be
guessed.
Clause-②: yes (widening) — measured on this diff: the published `studio`
catalogue of `@object-ui/i18n` gains ten keys and changes or removes
none.
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio
的「删除软件包」确认框现在列出会删掉什么:每个对象及其记录数、其他元数据按类型计数、未发布草稿数;选择「删除结构和数据」时多一行红色提示「表会被永久删除,不进回收站,无法恢复」。
- 为什么改:原来确认框只让你输名字、选是否保留数据,不说删哪些对象、各有多少行,也没说删表是永久的、进不了回收站。
- 风险与回滚:只读,纯界面:打开确认框时多发一次 `GET /api/v1/packages/:id/export` 和每个对象一次
`$top=0` 的数据计数;读失败只显示一行说明,删除照常可用。回滚即 revert。
- 先看:`PackagesPage.tsx` 里打开确认框时读取的那个 effect;`packageRemovalContents.ts`
如何把导出结果折成按类型计数;`PackageRemovalSummary.tsx` 永久删除提示的显示条件。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/PackagesPage.confirmFlows-11784.test.tsx`,预期
`Tests 20 passed (20)`。
- 需要你做的:无,席位落地。
## What changes
- **`ui/packages/app-shell/src/views/metadata-admin/PackagesPage.tsx`**:
pressing Delete package marks the list loading and opens the dialog;
while the dialog is open, an effect keyed on the package id reads `GET
/api/v1/packages/:id/export` (the page's existing `apiJson`), then
counts each object's rows through the data source the Console already
uses for record counts (`useAdapter().find(name, { $top: 0 })`, reading
`total`, as `objectViewLiveData.ts` and `DraftChangesPanel.tsx` do). The
adapter is read through a ref, so the effect keys on the id alone (ui
`AGENTS.md` #10). Closing the dialog cancels the reads in flight.
- **`packageRemovalContents.ts`** (new): reads the export answer into
what the dialog lists. Each collection key is folded to its singular
type with `canonicalMetaUrlType` (`@objectstack/spec/shared`);
`viewItems` (`ASSEMBLED_VIEW_ITEMS_KEY`, `@objectstack/spec/ui`) counts
as views; `withheldMaps`, the export's one declared list that is not a
collection, is skipped. Objects keep their name and label for the count
rows.
- **`PackageRemovalSummary.tsx`** (new): the list (each object with its
count, then each other type with its count), the pending-drafts line
from the drafts the sheet already reads, and the permanent-drop warning.
The warning shows only when the author chose to drop the data, and not
when the package is known to hold no object. A count the data list does
not answer reads "Record count unavailable", never `0`. An export the
server refuses reads "Could not read what the package holds. Deleting it
still removes everything in it.", and the delete stays available.
- **`ui/packages/i18n/src/namespaces/studio/{en,zh}.ts`**: ten
`engine.packages.detail.delete*` strings in both catalogues.
- **`.changeset/31025-delete-package-contents.md`**: `@object-ui/i18n`
patch (the namespace ships in its `dist`) with both declaration lines.
`@object-ui/app-shell` is private.
## Tests
Five cases in a new top-level `describe` of the shared delete harness,
`PackagesPage.confirmFlows-11784.test.tsx`, driving the real
`PackageDetailSheet` through clicks (Delete package, the mode radios,
the name input, Confirm):
- lists each object with its count (`1234 records`, `1 record`) and the
other items by type (views folded from `views` + `viewItems`,
dashboards, flows, object extensions); `withheldMaps` is not listed; the
drafts line; each count is `find(name, { $top: 0 })`; opening the list
writes nothing;
- the permanent line is absent with no mode and with *structure only*,
present after *structure and data*, and gone again on switching back;
- a package whose export holds no object draws no permanent line under
*structure and data*;
- a missing `total` and a rejected count both read "Record count
unavailable", not `0`;
- an export answering 500 says so, keeps the permanent line under
*structure and data*, and the DELETE is still sent (no query).
The harness gains a module-top `import '@objectstack/formula'` and
`import '@objectstack/lint'` (`check:test-source-alias` asks it of an
edited file whose source reaches them through a dynamic import), a
`vi.mock` of `@object-ui/react` whose `useAdapter` answers `null` unless
a case installs a data source, and an export answer the stub serves.
Ablations (`scripts/ablation-replace.mjs`, each anchor hit once, each
restored to the HEAD blob with `git diff HEAD` empty):
| mutation | red |
|:--|:--|
| permanent line ignores the chosen mode | the mode case, 1 failed / 19
passed |
| permanent line ignores "no object" | the no-object case, 1 failed / 19
passed |
| a missing `total` counted as `0` | the unknown-count case, 1 failed /
19 passed |
| `withheldMaps` not skipped | the listing case, 1 failed / 19 passed |
The fourth ablation's first attempt was refused by the tool (its
replacement text already occurred in the file) and restored; it was
re-run with a unique replacement.
## Acceptance notes
- **What the list does not show.** The export door skips a package's
datasources and email templates (`assemblePackageManifest`: "not
consumed by registerApp"), while the delete removes them too, so those
two types are not counted in the dialog. The dialog's description line
above the list is unchanged. Not filed; named here for the seat.
- **Whose count.** `total` is the rows the caller can read, the figure
the object list's footer shows. Dropping a table removes every row in
it, which can be more than the caller sees under row-level sharing.
- **Read gate.** The export door asks `studio.access` or `setup.access`;
the delete door asks `manage_metadata`. A caller who holds only the
second sees the "could not read" line and can still delete.
- **No new route** (seat ruling): both reads already exist, and neither
door changes.
- **The chat card** for `packageRemoval` is not in this PR (seat
ruling): the payload is produced by cloud's `delete_package`, the cloud
repository is out of this lane's reach, and nothing on `main` spells it.
## Verification
All local readings below are at head `d869c685` unless a line names
another (base `8ef4d309`, `origin/main` when the branch was cut). The
last commit only changed the changeset's `Clause-②:` arm; the families
that read a changeset were re-run on it.
<details><summary>Tests, type-check and lint</summary>
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/PackagesPage.confirmFlows-11784.test.tsx
packages/i18n/src/__tests__/studio-namespace.test.ts` (under the verify
lock, at `7dcc7b0d`, whose source equals this head's): Test Files 2
passed (2), Tests 38 passed (38).
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/
packages/i18n/src/__tests__/studio-namespace.test.ts` (at `c8a24f8c`,
before the harness's two module-top imports): Test Files 731 passed
(731), Tests 7780 passed, 1 skipped.
- `pnpm --filter @object-ui/app-shell run type-check` (`tsc --noEmit`
and `tsc -p tsconfig.test.json`, which compiles the harness): exit 0,
after `pnpm --workspace-concurrency=2 --filter
'@object-ui/app-shell^...' build` (VERDICT command-exit 0).
- `pnpm exec eslint` from `ui/` over the three app-shell source files:
exit 0, 0 errors, 15 warnings, the same 15 that `PackagesPage.tsx` on
`origin/main` carries alone (the new files add none).
- `pnpm --dir ui run check:new-line-citations`: 0 new citations.
`check:i18n-keys`, `check:i18n-drift`, `check:i18n-dead-keys`: exit 0
each; the dead-key report names none of the new keys.
</details>
<details><summary>Derived gates (dispatch-gates --commands, 36
families)</summary>
- All 36 run, exit codes captured before any pipe, all exit 0.
`dispatch-gates.mjs --ran`: "36 derived famil(ies) accounted for — 36
run, 0 NOT-MEASURED".
- The loop ran on `7dcc7b0d`; on `d869c685` the changeset readers were
re-run (`check-adr-0087-registration --base origin/main`,
`check-changeset-no-major`, `check-empty-changeset`,
`check:changeset-gate-self-tests`, `check:nul-bytes`,
`check-closing-keyword-parity`, `check:published-files`), all exit 0.
- First reading of `pnpm check:test-source-alias` was exit 1: the edited
harness reaches `@objectstack/formula` and `@objectstack/lint` through
the page's dynamic imports. Fixed by the two module-top imports; exit 0
after.
</details>
Not measured locally: a live Console check against a booted server (the
export answer in the pins is shaped from `PackageExportManifestSchema`),
and the CI-owned families (`Test UI`, `Lint UI`, `Type Check UI` and the
rest), read from this PR's check runs.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5kYWTJtoyM3pr9E5wF)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(i18n): Studio strings for what the Delete package dialog removes
6
+
7
+
Clause-②: yes (widening)
8
+
Metadata-protocol: none
9
+
10
+
The `studio` namespace gains the `engine.packages.detail.delete*` strings (en and zh) the Delete package dialog now draws: its heading for what the package holds, each object's record count (one, other, unavailable), the pending-drafts line, and the warning shown when the author chooses to drop the data, that the tables are dropped permanently and do not go to the recycle bin.
0 commit comments