Repository navigation
Commit 474b40e
fix(plugin-detail): show the action's option label on the record history badge (#28989)
domain:ui#4 · zhuangjianguo · session_01QsR2jR9wsxdgKzces31XWg
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbPt4BqVrm2MVcQn63SRJQJh)_
Fixes #28894
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- 改了什么:记录页「历史」时间线的动作徽章,不再把存储值大写后直接显示,而是显示来源字段选项的翻译标签。`HistoryTimeline`
新增可选属性 `actionField`(`{ object, field }`),用
`useSafeFieldLabel().fieldOptionLabel` 读服务端下发的翻译;`record:history`
自取数据时指明来源是 `sys_activity.type`,所以中文界面显示「已更新」「已创建」。
- 为什么改:中文用户打开任何记录的历史标签页,都看到英文大写的 UPDATED /
CREATED,旁边的摘要却是中文。翻译早已下发,只是徽章没有用。与卡片原文的偏差:徽章的值来自
`sys_activity.type`(`updated` / `created`),不是
`sys_audit_log.action`(`update` / `create`),席位已按此修订认领。
- 风险与回滚:没有翻译标签的值、以及宿主自行传入的条目,仍按存储值大写显示,英文界面外观不变。不改元数据、不改协议、不发新请求。回滚即还原本
PR。
- 先看:`ui/packages/plugin-detail/src/HistoryTimeline.tsx`(徽章的
`actionLabel` 与 `uppercase`
条件)、`ui/packages/plugin-detail/src/renderers/record-history.tsx`(`SELF_FETCH_ACTION_FIELD`)。
- 两分钟验证:`cd ui && pnpm exec vitest run
packages/plugin-detail/src/__tests__/HistoryTimeline.actionLabel-28894.test.tsx
packages/plugin-detail/src/renderers/__tests__/record-history.actionLabel-28894.test.tsx`,预期
6 个用例全绿。
- 需要你做的:无,席位落地。
## What changes
- **`HistoryTimeline`** takes an optional `actionField` prop, `{ object,
field }`, naming the select field the entries' `action` values come
from. Each badge shows `useSafeFieldLabel().fieldOptionLabel(object,
field, value, value)`: the option's label in the active language from
the served translations, the same read the record page makes for any
select option label. A value with no label, and every value when
`actionField` is not given, shows as stored. The `uppercase` class now
applies only to a value shown as stored, so a translated label is drawn
as the translation spells it.
- **`record:history`** passes `{ object: 'sys_activity', field: 'type'
}` for the entries it fetches itself, because its self-fetch maps each
`sys_activity` row's `type` to the entry's `action`. Entries a host
passes in name no source and keep the stored value.
- The badge tone (`ACTION_VARIANT`) still keys on the stored value. No
new request, no ui map of action labels.
## Why
The HotCRM lead page's History tab printed `UPDATED` / `CREATED` beside
Chinese summaries, although the option labels were already translated
and served.
**Deviation from the card's text (claim amendment 6568854052).** The
card names `sys_audit_log.action` as the label source. Measured at
`origin/main` `9a9823d9` and HotCRM `d4ffe99`, the reproduced badge
reads `sys_activity.type` instead:
- The lead History tab is an authored `record:history` block (HotCRM
`src/sales/pages/lead_detail.page.ts`). It has no host entries, so the
block self-fetches `sys_activity` and maps `action: r.type`.
- The badges seen, `UPDATED` / `CREATED`, are the `sys_activity.type`
values `updated` / `created`. `sys_audit_log.action` stores `update` /
`create`.
- The row text (更新了 线索 …:职位) is the `sys_activity` summary
(`plugin-audit` `activityUpdatedFields`).
- On the synthesized record page the `sys_audit_log` read feeds
`record:activity`, not `HistoryTimeline`.
So the badge reads the option labels of `sys_activity.type` (zh-CN: 已更新
/ 已创建). The card's intent is unchanged: labels come from the declared
options through the served translations, the raw value is the fallback,
and the ui holds no map.
## Tests
- New `HistoryTimeline.actionLabel-28894.test.tsx`: zh shows 已更新 / 已创建
without `uppercase`; a value with no option label shows as stored,
upper-cased; en keeps the upper-cased look where the label is the stored
word; no `actionField` shows every value as stored. The bundle is built
with `transformSpecTranslations`, the transform the console applies to
`/api/v1/i18n/translations/:locale`.
- New `record-history.actionLabel-28894.test.tsx`: the self-fetch labels
its badges from `sys_activity.type` in zh; host entries keep the stored
value.
- Ablations, each through `scripts/ablation-replace.mjs`, with the blob
restored to HEAD and `git diff HEAD` empty afterwards:
- `record-history` stops passing the source (`actionField={undefined}`):
1 red, the self-fetch zh case.
- The label read is removed (`fieldOptionLabel(…)` replaced by the
stored value): 2 red, the zh cases of both files.
- `uppercase` applied unconditionally: 2 red, the zh cases of both
files.
## Acceptance notes
- Acceptance 1 (zh labels): at head `f7a834c5`, a `pnpm dev:crm --
--fresh` server, signed in as the dev admin, served `GET
/api/v1/i18n/translations/zh-CN` with
`objects.sys_activity.fields.type.options` = `created: 已创建, updated:
已更新, …` (twelve options). The en bundle serves each option as its value
(`updated: updated`). The console flattens that document into the
`fieldOptions` keys `fieldOptionLabel` reads, which is the shape the
unit tests build. The served document is in the project files at
`issue-28894/served-zh-CN-translations.json`. NOT MEASURED: a browser
run of the built console on a record's History tab, because it needs
`pnpm console:build`.
- Acceptance 2 (fallback): a value with no option label (for example a
custom `activityMilestones[].type`) shows as stored.
- Acceptance 3 (`uppercase`): it applies only to a value shown as
stored. English badges look as before, because the en label is the
stored word.
- `HistoryTimelineProps`, which is exported, gains one optional member
(`actionField`), and the package entry now exports its type
`HistoryActionField`. That widens a published type, so the declaration
is `Clause-②: yes (widening)` (claim amended on the card); the changeset
stays `patch` with `Metadata-protocol: none`.
- Outside this card: `DetailView`'s own history tab
(`schema.history.entries`) names no source, so its badges keep the
stored value. `ACTION_VARIANT` keys are `sys_audit_log` actions
(`update`, `create`, …), so every `sys_activity` entry gets the neutral
outline tone, as it did before this PR. Noted, not filed.
## Verification
<details><summary>Local runs</summary>
- At head `f7a834c5` (before merging main):
- `pnpm exec vitest run` over all 363 `ui/packages/plugin-detail` test
files, in three batches: Test Files 362 passed and 1 skipped; Tests 3195
passed and 18 skipped.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 34 derived commands, all exit 0. `--ran`
reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. These
include `check-sdui-manifest.mjs`, `check-changeset-no-major.mjs --base
origin/main`, `check-adr-0087-registration.mjs`,
`check:type-check-debt`, `check:published-files`, `check:nul-bytes`, and
`pnpm --dir ui exec vitest run scripts/` (Test Files 169 passed, 2
skipped; Tests 4900 passed, 2 skipped).
- At head 64c9ef5 (main `62b3ec36` merged, clean):
- The six history test files: Test Files 6 passed, Tests 45 passed.
- `turbo run type-check --filter=@object-ui/plugin-detail`: 48 of 48
tasks successful. The script runs `tsc -p tsconfig.test.json` as well,
so it covers the tests.
- `dispatch-gates --commands` derived the same 34 commands; all 34 exit
0 again (the PR-body gates read this body). `--ran`: 34 derived, 34 run,
0 NOT-MEASURED, 0 UNRUN. `vitest run scripts/`: Test Files 169 passed, 2
skipped; Tests 4900 passed, 2 skipped.
- At head `9ec21e47` (main `00d0668d` merged, clean): nothing re-run.
The incoming files touch no file under `ui/packages/plugin-detail`,
`ui/packages/i18n` or `ui/packages/react`. They include #28895's audit
log badge (`ui/apps/console/src/pages/system/`), which labels from the
served `sys_audit_log` document. This PR does not touch that page.
- At head `cb4cac0c` (declaration rework: `HistoryActionField` exported,
`Clause-②: yes (widening)`): `dispatch-gates --commands` derived the
same 34 commands. All 34 exit 0, and the PR-body gates read this body.
`--ran`: 34 derived, 34 run, 0 NOT-MEASURED. `turbo run type-check
--filter=@object-ui/plugin-detail`: 48 of 48 tasks.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's run.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: not applicable.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 section is
the Chinese summary the repo asks for).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jR9wsxdgKzces31XWg
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jR9wsxdgKzces31XWg)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4df6292 commit 474b40e
6 files changed
Lines changed: 230 additions & 5 deletions
File tree
- .changeset
- ui/packages/plugin-detail/src
- __tests__
- renderers
- __tests__
| 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 | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
22 | 25 | | |
23 | 26 | | |
24 | 27 | | |
| |||
34 | 37 | | |
35 | 38 | | |
36 | 39 | | |
| 40 | + | |
37 | 41 | | |
38 | 42 | | |
39 | 43 | | |
| |||
59 | 63 | | |
60 | 64 | | |
61 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
62 | 76 | | |
63 | 77 | | |
64 | 78 | | |
| |||
79 | 93 | | |
80 | 94 | | |
81 | 95 | | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
82 | 107 | | |
83 | 108 | | |
84 | 109 | | |
| |||
179 | 204 | | |
180 | 205 | | |
181 | 206 | | |
| 207 | + | |
182 | 208 | | |
183 | 209 | | |
184 | 210 | | |
185 | 211 | | |
186 | 212 | | |
187 | 213 | | |
188 | 214 | | |
| 215 | + | |
189 | 216 | | |
190 | 217 | | |
191 | 218 | | |
| |||
221 | 248 | | |
222 | 249 | | |
223 | 250 | | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
224 | 255 | | |
225 | 256 | | |
226 | 257 | | |
| |||
253 | 284 | | |
254 | 285 | | |
255 | 286 | | |
256 | | - | |
257 | | - | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
258 | 292 | | |
259 | 293 | | |
260 | 294 | | |
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
169 | | - | |
| 169 | + | |
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
64 | 73 | | |
65 | 74 | | |
66 | 75 | | |
| |||
233 | 242 | | |
234 | 243 | | |
235 | 244 | | |
236 | | - | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
237 | 252 | | |
238 | 253 | | |
239 | 254 | | |
| |||
0 commit comments