Repository navigation
Commit 501c864
feat(app-shell): Setup pages for position holders and permission-set assignment (#28310)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6Jao8LAaW8BQegvVaxCTUNw8Zk)_
Part of #28241
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:Setup
新增两个页面——「岗位持有人」(选一个岗位,查看、添加、移除持有人)和「权限集分配」(选一个权限集,查看直接持有和经岗位持有的用户,分配或取消分配)。两页直接复用岗位详情页和权限集编辑器里现有的那两个区块,选中的项写进
URL。
- 为什么改:给人分配岗位和权限集是管理员的日常工作,现在只能进元数据详情页里做;目录迁到 Studio(N-26)后,管理员需要在 Setup
里有自己的入口,不必为加一个持有人打开 Studio。
-
风险与回滚:只新增页面和翻译,不改任何读写;谁能增删、服务端的拒绝(含委派管理员越界)都沿用区块原样,原文显示。详情页里的两个区块照常工作。回滚即
revert 本 PR。
-
先看:`ui/packages/app-shell/src/views/setup/CatalogAssignmentPage.tsx`(目录读取失败时如实报错而非显示空列表)、`ui/packages/app-shell/src/services/builtinComponents.tsx`
新增的两个注册。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/setup/`,预期全绿;或在本地 Console 打开
`/apps/setup/component/security/position-holders`,左侧列出岗位,点一个看到持有人。
- 需要你做的:无,席位落地。注意 Setup
左侧导航里的两个入口属于服务端安全插件的导航贡献,不在本车道,已作为发现交席位另立;入口落地前,两页只能经 URL 打开。
## What changes
Two Setup component pages, registered in
`services/builtinComponents.tsx` the way Packaged Automation and
Connector Credentials are:
- **Position holders** (`security:position-holders`,
`/apps/setup/component/security/position-holders`): the registry's
`position` catalog on the left with a filter; the picked position's
`PositionHoldersSection` on the right.
- **Permission set assignment** (`security:permission-set-assignment`,
`/apps/setup/component/security/permission-set-assignment`): the
registry's `permission` catalog; the picked set's
`AssignedUsersSection`, in the bordered box the permission set editor
draws around it.
Both share `views/setup/CatalogAssignmentPage.tsx`:
- The picked item rides a page-scoped query parameter (`?position=` /
`?set=`, constants in `views/setup/peopleAssignments.ts`), written with
`replace`, so a shared or refreshed link opens the same item (ui
AGENTS.md #8).
- The catalog is read through the metadata store's `ensureType`. The
store answers a failed list with an empty one and records the failure on
the type's status, so the page reads `getTypeStatus` and says "Could not
read the positions." with a retry (`refresh(type)`, past the store's
failure cooldown), never "No positions".
- A URL naming an item the catalog does not hold says so, and reads no
holders for it.
- The store is read through a ref, so no read keys on the context
value's identity (ui AGENTS.md #10).
- The section is keyed by the item's name, so its local state (an open
confirm, an inline refusal) belongs to that item and is not carried to
the next one.
The two sections are imported, not moved or rewritten. Who may add or
remove (the section's grant check on `sys_user_position` /
`sys_user_permission_set`), the member-only picker and every server
refusal stay the section's own. The item pages keep rendering them until
N-28 retires those routes.
20 new keys under `setupAssignments` in all ten packs, one contiguous
block per pack after `connectorCredentials`. The de pack's `notFound`
adds one correctly paired „…“ span, so `de-quote-pairing-3876.test.ts`
counts 102 instead of 101, with the reason line in its ledger comment.
Changeset: `.changeset/28241-setup-people-assignment-pages.md`,
`@object-ui/app-shell` and `@object-ui/i18n` patch.
## Tests
- `views/setup/PositionHoldersPage.test.tsx` (8): lists the catalog and
reads no holder before a pick; picking writes `?position=` and lists
holders; add round-trip (create by name, list reads the new holder
back); remove round-trip (confirm, delete by row id, list reads it gone
and shows the empty line); a delegated administrator's out-of-scope
refusal (`DELEGATION_SCOPE_VIOLATION`, 403) shown as the server's
sentence, verbatim; a failed catalog read shown as a failure, then a
retry through `refresh('position')`; an unknown `?position=`; the
filter.
- `views/setup/PermissionSetAssignmentPage.test.tsx` (5): the same for
sets, including that a holder through a position stays when a direct
grant is unassigned.
- `views/setup/peopleAssignments.navContribution.test.tsx` (4): both
refs are registered by the production registration module, address
`component/security/…`, and mount through the component route.
- `views/setup/peopleAssignments.test.ts` (3): `catalogEntries` and
`filterCatalogEntries`.
- CONTROL, unchanged and green:
`ResourceEditPage.setupCatalog-7611.test.tsx` and
`PermissionMatrixEditor.setupCatalog-7611.test.tsx` (the sections still
render on the item pages), plus every `PositionHoldersSection*` /
`AssignedUsersSection*` test and `setupHoldersGrant-12082.test.tsx`.
- Ablation (one-off, on the head before the literal-key commit, which
does not touch that file; restored by `scripts/ablation-replace.mjs`,
blob back to HEAD): deleting the `getTypeStatus(type) === 'error'`
branch in `CatalogAssignmentPage.tsx` turns exactly "says a failed
catalog read failed, never "no positions", and retries past the cache"
red (1 failed, 7 passed); restored, 8 passed.
## Acceptance notes
- **The Setup navigation entries are not in this PR.** Setup's nav is
server metadata: `group_people_org` is an empty anchor in
`packages/platform-objects/src/apps/setup.app.ts`, and the Positions /
Permission Sets entries are the `navigationContributions` of
`packages/plugins/plugin-security/src/security-plugin.ts` (group
`group_access_control`). The two entries (`type: 'component'`,
`componentRef: 'security:position-holders'` and
`'security:permission-set-assignment'`, group `group_people_org`) belong
there, outside this ui lane. Hence `Part of`; the card's "both pages
reachable from Setup nav" closes when that entry lands. Until then the
pages open only by URL.
- "Refused verbatim" reads the sentence as the console shows every write
refusal: the server's text without the `[Security] ` machine prefix
(`extractWriteErrorMessage`). The tests pin that exact string.
- The pages list the audience anchors (`everyone`, `guest`) like any
other position, as the item pages do; the page adds no rule of its own
about them.
- The section components still carry their own inline English / Chinese
copy (`useMetadataLocale`); only the page chrome uses the ten-language
packs.
## Verification
Local, against head `eab3e80a`, on a shared box:
<details><summary>Tests and type-check</summary>
- `pnpm --dir ui exec vitest run packages/app-shell/src/views/setup/
…item-page and section tests… packages/i18n/` → 119 files, 1691 passed,
before the de pin and literal-key fixes; after them `packages/i18n/
packages/app-shell/src/views/setup/` → 105 files, 1577 passed, 13
skipped, exit 0.
- `pnpm --dir ui exec vitest run
scripts/__tests__/check-i18n-call-site-keys.test.ts
scripts/__tests__/check-i18n-dead-keys.test.ts
packages/app-shell/src/views/setup/` → 14 files, 427 passed, exit 0.
- `pnpm --filter @object-ui/i18n run type-check && pnpm --filter
@object-ui/app-shell run type-check` → exit 0.
- `pnpm --dir ui check:new-line-citations` → 0 new citations, exit 0.
</details>
<details><summary>Derived gates (dispatch-gates --commands, 34
lines)</summary>
```text
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-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-corpus.mjs :: 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-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: 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:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran LIST` → 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. The ui
scripts suite read 169 files passed, 2 skipped (4901 tests).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibvdtrVLjcNWmcBoHnP)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3d9175d commit 501c864
21 files changed
Lines changed: 1140 additions & 3 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- services
- views/setup
- i18n/src
- __tests__
- locales
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
27 | 30 | | |
28 | 31 | | |
29 | 32 | | |
| |||
164 | 167 | | |
165 | 168 | | |
166 | 169 | | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
167 | 190 | | |
168 | 191 | | |
169 | 192 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
0 commit comments