Repository navigation
Commit 9a9823d
fix(example-crm): full-name contact titles, a complete zh-CN bundle, declared highlight strips and a user-typed lead owner (#28886)
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9Ubb23RtUpCQPUEM3SvkuwNQzr)_
Fixes #28870
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:改 `examples/app-crm` 的元数据,另把
`scripts/i18n-coverage-baseline.json` 里 app-crm 的未翻译上限从 101 写成
0。联系人标题改为全名(`nameField: 'full_name'`,公式声明 `returnType:
'text'`);五个对象的复数名去掉「列表」;zh-CN 词条补齐导航、字段、选项、仪表板、数据集、对象描述、校验提示和欢迎页(`os
lint --i18n-strict` 未翻译数 101 → 0);客户、联系人、商机、线索、活动各声明
`highlightFields`(不含阶段字段,阶段已由阶段路径显示);线索「分配给」改为用户字段;误挂在 Acme 下的
"Discovery Call — Bletchley Systems" 改名为 "Discovery Call — Acme Platform
Renewal";仪表板说明改写成业务语言。
- **为什么改**:#26676 裁决 B 把 app-crm
定为预览面,它的元数据错误在评审里被当成平台缺陷。维护者裁决:「联系人标题只显示
“Ada”,不处理,这种就是元数据的bug」,所以平台不动,只修元数据。
- **风险与回滚**:风险低。示例包是私有包,不发布;平台代码零改动。种子不再给线索写
`assigned_to`(原值是客户联系人的邮箱,不是用户),新库里该列为空,要在界面上分配。回滚即 revert 本 PR。
- **先看**:① `examples/app-crm/src/objects/lead.object.ts`(`assigned_to`
改为 `Field.user`,`is_closed` 声明 `boolean`);②
`examples/app-crm/src/data/index.ts`(去掉种子里的邮箱、活动改名);③
`examples/app-crm/src/objects/contact.object.ts`(标题字段)。
- **两分钟验证**:`pnpm dev:crm -- --fresh -p 38427`,浏览器语言设为中文后登录:侧栏是「线索 / 客户
/ 联系人 / 商机 / 活动 / 销售漏斗」,联系人页标题是 "Ada Lovelace",页签没有「列表」二字。
- **需要你做的**:无,席位落地。
## What changes
Every metadata edit is in `examples/app-crm`, per the ruling: no
platform change, no lint, no inference, no gate. The one other file is
the app-crm row of the i18n coverage baseline.
1. **Contact title.** `crm_contact` declares `nameField: 'full_name'`,
and the `full_name` formula declares `returnType: 'text'`, which makes
it title-eligible. Title, lookups and pickers read "Ada Lovelace".
2. **Plural labels.** The zh-CN `pluralLabel` of account, contact,
opportunity, lead and activity is the plain noun (客户, 联系人, 商机, 线索, 活动).
3. **zh-CN bundle.** Navigation entries keyed by id (`nav_leads` …
`nav_pipeline_dashboard`), every declared field label, every select
option (industry, stage, approval status, lead status and source,
activity type and status), the line-item object, the `crm_convert_lead`
action, the dashboard title, description and six widgets, the
`opportunity_metrics` dataset (tile and axis names), object
descriptions, the five validation messages and the welcome page.
4. **Highlight fields.** Picks, at most 6, never the title field, and
never the field the record page already draws as the stage path
(`detectStatusField`: opportunity `stage`, lead and activity `status`),
so the stage is not shown twice once #28868 shows a declared strip as
declared:
- `crm_account`: `industry`, `annual_revenue`, `website`
- `crm_contact`: `account`, `email`
- `crm_opportunity`: `account`, `amount`, `close_date`, `probability`
- `crm_lead`: `company`, `source`, `lead_score`, `assigned_to`
- `crm_activity`: `type`, `due_date`, `contact`, `account`
5. **Lead owner.** `crm_lead.assigned_to` is `Field.user` (target
`sys_user`). The seed no longer writes the four email addresses into it;
see the deviation below.
6. **Seed ownership.** The activity "Discovery Call — Bletchley Systems"
was linked to Acme Corp, Ada and the Acme renewal. It is renamed
"Discovery Call — Acme Platform Renewal" to match its links.
7. **Dashboard copy.** The widget and dashboard descriptions are
rewritten in business words (no `compareTo`, no chart-type notes) and
translated.
Also, as a bounded in-place fix of the same class as item 1 (a formula
without its result type), `crm_lead.is_closed` declares `returnType:
'boolean'`. The zh-CN lead page showed the raw word "false" under 已关闭;
it now shows 否.
### Deviation from the card (seat ruling B)
The card asks for "seeded user references" on `assigned_to`. Measured on
`45efba2a`: no `sys_user` row exists when app-crm's seed runs. The dev
admin is minted at `kernel:ready`, after `AppPlugin.start()` seeds, and
people otherwise come from sign-up
(`examples/app-showcase/src/security/demo-personas.ts`;
`content/docs/data-modeling/seed-data.mdx`, "Owner-style fields"). The
only way to resolve a seeded user reference would be for app-crm to seed
`sys_user` rows itself. Those are directory rows with no login, and
`packages/plugins/plugin-auth/src/auth-manager.ts` (the `#14157`
docblock) records that seeded people close the first-visitor bootstrap
sign-up under `invite_only`. The seat ruled option B: `Field.user`, and
the seed leaves the field unset, which is the documented owner-style
pattern. The acceptance screenshot of the lead list was taken after
assigning three leads in the UI.
## Tests
- `pnpm --filter @objectstack/example-crm typecheck`: exit 0.
- `pnpm --filter @objectstack/example-crm test`: 7 files, 48 tests
passed.
- `os lint --i18n-strict` on the app: 0 errors (was 101 untranslated
declared strings). The 4 remaining `field-no-consumers` warnings were on
the base too; the base had 9, and the highlight strips and the contact
title now consume 5 of them.
- `os validate`: exit 0.
- Fresh boot (`pnpm dev:crm -- --fresh`): seed loads 28 rows with no
unresolved-reference line.
## Acceptance notes
- Screenshots (1440 px, zh-CN, fresh boot; 375 px contact page too) are
held in the maintainer's project folder `issue-28870/` and are not
uploaded to GitHub: `zh-account-page.png`, `zh-contact-page.png`,
`zh-lead-list.png`, plus the lead, opportunity, activity and dashboard
pages and the account and contact lists. The seat read them before
landing.
- `crm_contact.full_name` is a text formula, so the title is
display-only: a sort by it is refused and search does not reach it.
Search still finds a contact by `last_name` and `email` (both
`searchable`). The contact list and record page load and render the full
name; see the screenshots. A stored text field with an `expression`
would make the title searchable; the card ruled the formula, so this PR
keeps it.
- zh-CN `assigned_to` reads 分配给, because the platform's `owner_id`
already reads 负责人 on the same page.
- `scripts/i18n-coverage-baseline.json`: the app-crm entry is written
down from 101 to 0 by `node scripts/check-i18n-coverage.mjs --update`
(claim amended to add this one entry), so a regression cannot creep back
under the old ceiling. No other entry moved.
- No changeset: `@objectstack/example-crm` is private and publishes
nothing, so the PR carries `skip-changeset`.
## Verification
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at the final head: 42 gates (34 for the app
files, 8 more for the baseline file under `scripts/`), each run with
exit 0 and reconciled with `--ran`, 0 NOT-MEASURED. The PR-body gates
ran against this body through `GITHUB_EVENT_PATH`.
</details>
Dogfood battery: not owed (no `!` in the title and no `(narrowing)`
arm).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jRrzBxwrREVnKhpyXn)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2c70f65 commit 9a9823d
9 files changed
Lines changed: 220 additions & 27 deletions
File tree
- examples/app-crm/src
- dashboards
- data
- objects
- translations
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
58 | | - | |
| 58 | + | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
| 75 | + | |
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
94 | | - | |
| 94 | + | |
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
129 | | - | |
| 129 | + | |
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
62 | 65 | | |
63 | 66 | | |
64 | 67 | | |
65 | 68 | | |
66 | 69 | | |
67 | 70 | | |
68 | | - | |
69 | 71 | | |
70 | 72 | | |
71 | 73 | | |
72 | 74 | | |
73 | 75 | | |
74 | 76 | | |
75 | 77 | | |
76 | | - | |
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
82 | 83 | | |
83 | | - | |
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
99 | 98 | | |
100 | 99 | | |
101 | 100 | | |
| |||
106 | 105 | | |
107 | 106 | | |
108 | 107 | | |
109 | | - | |
| 108 | + | |
110 | 109 | | |
111 | 110 | | |
112 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
17 | 24 | | |
18 | 25 | | |
19 | 26 | | |
| |||
28 | 35 | | |
29 | 36 | | |
30 | 37 | | |
| 38 | + | |
| 39 | + | |
31 | 40 | | |
32 | 41 | | |
33 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
73 | 76 | | |
74 | 77 | | |
75 | 78 | | |
76 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
77 | 82 | | |
78 | | - | |
79 | 83 | | |
80 | 84 | | |
81 | 85 | | |
| |||
89 | 93 | | |
90 | 94 | | |
91 | 95 | | |
| 96 | + | |
| 97 | + | |
92 | 98 | | |
93 | 99 | | |
94 | 100 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
0 commit comments