Repository navigation
Commit 18870c9
feat(console)!: an administrator's first-run checklist on Home replaces the currency and time zone dialog (#30868)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoPNS2FEPAcwYy3HAGUQ4J7c)_
Fixes #30206
Clause-②: yes (narrowing) — the published `@object-ui/i18n` packs (all
ten) drop the 14 `console.workspaceTimezonePrompt.*` keys, which had no
reader outside the removed dialog. The widening half: new console i18n
keys `console.firstRunChecklist.*` and
`console.settingsView.invalidFormat` / `.invalidFormatHelp` /
`.invalidFormatPick` (all ten packs)
Metadata-protocol: none
Supersedes #30552 (same change, taken over from a branch whose account
is out of tokens: cut from `claude/project-thread-5murn7` @ `2dc81e70`,
then `origin/main` merged).
## 维护者速读
-
改了什么:管理员打开首页(/home)时,顶部出现一张「完成初始设置」清单(组织名称、默认语言和国家、系统时区、默认货币、邮件发送、邀请同事),原来一进系统就弹出的时区/货币对话框删掉了;在默认国家里输入「中国」现在能保存为
CN,格式错误的提示改成读者语言。
-
为什么改:维护者原话「加一个首次配置清单:目前启动时就弹出选择货币很奇怪,一起处理」;新部署的管理员不知道先设什么,默认语言、国家和货币还互相矛盾。
- 风险与回滚:只动控制台、语言包和两处 e2e 准备脚本,不动服务端;清单只读设置,只有点「确认」才写,写的就是设置页同一个
PUT。语言包删掉对话框的 14 个键,属 TypeScript 层面的收窄,changeset 写明了迁移。首屏英文语言包 57,233 /
60,000 gz,上限未动。回滚即 revert 本 PR,弹窗回来。
-
先看:`ui/apps/console/src/pages/settings/FirstRunChecklist.tsx`(读取与只在确认时写入)、`firstRunChecklistLogic.ts`(每行何时算完成)、`SettingsField.tsx`
的 `optionByName`(输入名称匹配代码)。
- 两分钟验证:`os dev --fresh` 后用管理员登录,打开
/home,顶部应有清单;中文浏览器下点「默认语言和国家/地区」的「设置」,在国家框输入「中国」后点「确认」,应提示已保存、该行打勾,Setup
页不再弹任何对话框。
- 需要你做的:改动 4,694 行(42 个文件,+2,671 / −2,023),超过 3,000
行人工合并线,请在席位复核通过后批准合并。
## What changes
**The dialog is gone; Home carries an administrator's first-run
checklist.** `WorkspaceTimezonePrompt.tsx` and its mount in
`AppContent.tsx` are removed. `FirstRunChecklist` is drawn at the top of
Home by the console's Home route (`App.tsx`), above the page's own
sections, inside the `ConsolePermissionsScope` that route already sits
under. Its logic, `firstRunChecklistLogic.ts`, is the dialog's logic
widened to every row (git shows it as a delete and an add; it keeps
`browserTimeZone`, `browserLanguage`, `browserCurrency`,
`isAdmissibleValue` and `mayWrite`).
**Row list** (the card leaves it to the PR; the seat kept Home as the
only placement). Each row's state is read from the real setting every
time it is drawn, never from a click:
| # | Row | Done when | Action |
|---|---|---|---|
| 1 | Organization name | the active organization's name is no longer
`Default Organization`, the name plugin-auth gives it (#30125 row 1) |
link to Setup → Organization record |
| 2 | Default language and country | `localization.locale` and
`.default_country` both resolve from a rung other than the manifest
default | edit in place, pre-filled from the browser |
| 3 | System timezone | `localization.timezone` resolves from a rung
other than the default | edit in place, pre-filled with the browser zone
|
| 4 | Default currency | `localization.currency` holds any value (it has
no platform default) | edit in place, pre-filled with the browser
region's currency |
| 5 | Email delivery | `mail.provider` is not `log`, `from_email` is
set, and SMTP has `smtp_host`; shown only to a session that may write
mail | link to Setup → Mail Delivery |
| 6 | Invite colleagues | a second member or a pending invitation | link
to Setup → Users (Invite User) |
A done row shows its value; a row whose state cannot be read (mail
unreadable, members unreadable) is left out rather than shown as not
done. Once every row shown is done the checklist draws nothing, and it
comes back on its own if a setting is unset again.
**The rulings the dialog was built on still hold** (objectui#11693 B2,
#28872). The browser only pre-fills rows 2 to 4. Nothing is written
until the row's Confirm, which is `saveSettingsNamespace('localization',
changes)`, the Settings page's own `PUT /api/settings/localization`.
Cancel writes nothing. An unset currency stays on the list until it is
set. Language, country and currency are one offer read from the
browser's first language (`zh-CN` → 中文, CN, CNY), so they cannot
contradict each other.
**Who sees it.** A session that may write `localization` (its manifest's
`writePermission`, failing closed on an unreported answer). A member
sees nothing and sends one `GET /api/settings` (the list, which never
refuses) and no namespace read. The close button hides it for that
administrator, in that workspace, on that device (`localStorage`
`os:first-run-checklist:ORG:USER`); nothing is written to the server and
no row changes. Its reads wait for the console's first paint
(`whenBootPainted`, objectui#12980), as the dialog's did.
**「中国」 in 默认国家.** The refusal started in the console: `DomainCombobox`
swapped typed text for a code only when it equalled the option label
(「中国 (CN)」) or the code, so 「中国」 went to the server as typed, and the
server's two-letter pattern refused it with `invalid_format` in English.
Now a typed name that exactly one option carries (the label without its
own code beside it, trimmed, case-insensitive) is committed as that
option's code; any other text is still committed verbatim and the server
stays the judge. A refusal with `code: 'invalid_format'` is now drawn in
the console's words and the reader's language, naming the setting by the
label its row draws (`settingsFieldRefusals.ts`): "Choose a value for
Default country from the list." for a picker, and "Gateway model is not
in a valid format. Use provider/model." for a typed field, repeating the
field's own help line because the refusal takes its place. Every other
refusal code keeps the server's sentence. The server's pattern check is
unchanged (⛔ `packages/**`).
**The narrowing.** `@object-ui/i18n`'s exported `TranslationKeys`
(`typeof en`) loses the 14 `console.workspaceTimezonePrompt.*` keys in
all ten packs. The changeset is `feat(console)!`, carries the BREAKING
note, the removed-key table and the ADR-0087 disposition `not-required
(runtime-interface-only
ui/packages/i18n/src/locales/en.ts#TranslationKeys)`, as on PR #29359.
**Eager language pack.** The checklist's strings sit in the console
group of the eager packs. After PR #30640 moved the marketplace pages'
strings into the lazy `marketplace` namespace, `i18n-locale-en` measures
57,233 gz against its unchanged 60,000 ceiling on this branch. This PR
adds no marketplace-page strings, so nothing moves into that namespace.
**Files beyond the claim's first surface**, each a reader of what was
removed (claim amendments 2 and 3):
`ui/apps/console/src/pages/auth/SetupPage.tsx` (imports
`browserTimeZone` from the renamed module),
`ui/apps/console/src/loadLocales.ts` (comment), the four console tests
that mocked the dialog's module, `ui/e2e/live/global-setup.ts` and
`examples/app-showcase/e2e/global-setup.ts` (each waited up to 60 s for
the dialog's testid before a run; the checklist is not modal, so that
step is removed), `docs/qa/platform-checklist/RUNNER.md` (told QA
runners to dismiss the dialog), and three i18n pins that named the
dialog's keys or file (`machineLocaleCensus-9909`,
`console-copy-system-noun-12956`) or count the de pack's quotes
(`de-quote-pairing-3876`, 105 → 107 for two new „…“ pairs).
## Tests
-
`ui/apps/console/src/pages/settings/__tests__/FirstRunChecklist.test.tsx`
(10), on the real `api.ts` against a stubbed `fetch` and the real
`MePermissionsProvider`: a member reads the list only and sees nothing;
no capability answer or an empty one sends no request; a fresh workspace
shows all six rows not done with their links and writes nothing; rows
the server reports as set are done and a fully set workspace draws
nothing; Set pre-fills the currency from the browser, nothing is written
until Confirm, and Confirm sends exactly one PUT with `{ currency: 'CNY'
}`; language and country are one offer (`{ locale: 'zh-CN',
default_country: 'CN' }`); Cancel and the close button write nothing and
the hide survives a remount with no request; an `invalid_format` refusal
is shown in the console's words and the row stays not done; zh copy; no
read while the boot splash is up.
- `firstRunChecklistLogic.test.ts` (11): `localeOffer`, `browserRegion`,
the admissible pre-fill, `isKeySet`, row done, `isMailConfigured`,
`isOrganizationNamed`, `hasInvitedColleagues`, the dismissal record and
unreadable storage.
- `SettingsField.domainOptionTypedName-30206.test.tsx` (5): 「中国」 commits
`CN`; trimmed and case-insensitive with full-width brackets; a name two
options carry and part of a name stay verbatim; a currency name commits
its code.
- `settingsFieldRefusals-30206.test.ts` (5), against the shipped `en`
templates.
- Updated: `SettingsView.field-errors`,
`SettingsView.crypto-unavailable` and `SettingsView.refusalToast-13146`
asserted the server's English sentence for `invalid_format`; they now
assert the console's sentence (and that the server's is gone).
- Removed with the dialog: `WorkspaceTimezonePrompt.test.tsx`,
`.currency-28872.test.tsx`, `.afterBootPaint-12980.test.tsx`; their pins
(only Confirm writes, the browser zone pre-fill, the currency arm, the
boot-paint wait, members never asked) are carried by the new files
above.
## Acceptance notes
- **Placement.** Home only, by the seat's call (claim amendment 2 on the
card). Home is the environment's own home (ObjectUI ADR-0075) and every
deployment that declares no landing app opens there after sign-in. A
deployment whose app declares `isDefault` lands its administrator in
that app, and the checklist is then seen on the next visit to Home, not
at sign-in.
- `DEFAULT_ORGANIZATION_NAME` is a copy of plugin-auth's literal
(`ensure-default-organization.ts`). No test holds the two equal: a `ui/`
test reading `packages/**` source would be a cross-package input outside
both CI rosters. A rename on the server side would make row 1 read
"named" for an organization nobody named.
- Refusal codes other than `invalid_format` (for example `value_domain`)
still carry the server's English sentence; localizing
`FieldError.message` is a server concern and out of this card.
- The dialog's old "asked once" record
(`os:workspace-timezone-prompt:*`) is no longer read; it stays in
browsers that have it, harmlessly.
-
`packages/plugins/plugin-auth/src/dev-admin-seed-workspace-timezone.test.ts`
still describes the dialog in its doc comment (the condition it pins is
still the one row 3 reads). Left alone: ⛔ `packages/**` on this card.
- objectui#11693 is outside this session's repository scope; its ruling
B2 is quoted from the code comments that carried it.
- The branch is the card thread's platform-assigned
`claude/project-thread-03bt30`, not `claude/issue-30206-…`. The eight
commits carried over from #30552 name the card in their subjects;
landed-branch history is not rewritten.
- #30125 rows 1 and 3 are covered here, as the card says.
## Verification
All readings on head `e823d8fd9d` unless a line names another.
<details><summary>Eager closure (Console vite build,
VITE_BASE_PATH=/console/)</summary>
- `pnpm --dir ui run check:eager-closure` exit 0: eager closure 2157.8
KB gz across 103 of 2412 chunks (budget 2431.6 KB). `i18n-locale-en`
57,233 gz (from `ui/apps/console/dist/eager-closure.json`) against the
unchanged 60,000 ceiling, headroom 2,767. The checker's ceilings are
byte-identical to `main` (no diff to `check-eager-closure-budget.mjs`).
</details>
<details><summary>Tests and type-check</summary>
- `pnpm --dir ui exec vitest run --maxWorkers=2 apps/console/
packages/i18n/` (lock VERDICT command-exit 0): Test Files 402 passed
(402), Tests 4540 passed, 13 skipped (4553).
- `pnpm --filter @object-ui/i18n type-check`, `pnpm --filter
@object-ui/console type-check`, `pnpm --dir ui run type-check:e2e`,
`pnpm --filter @objectstack/example-showcase typecheck`: all four exit 0
(one locked run, VERDICT command-exit 0).
- `pnpm --dir ui run check:new-line-citations`: 0 new citations.
</details>
<details><summary>Full dogfood battery (Clause-② narrowing
arm)</summary>
- `pnpm --filter @objectstack/dogfood test`, unsharded, after a full
`turbo run build --filter='!@objectstack/docs'` (118 of 118 tasks): lock
VERDICT command-exit 0, Test Files 424 passed, 3 skipped (427), Tests
3538 passed, 41 skipped (3579), no failures.
</details>
<details><summary>Derived gates: node scripts/pm/dispatch-gates.mjs
--commands (58) and --ran</summary>
- 58 of 58 exit 0, each run with stdin closed and its exit code recorded
before any pipe. `pnpm --dir ui exec vitest run scripts/` first answered
99 (lock not reached while the console tests held it) and was re-run
under the same slot: Test Files 170 passed, 2 skipped; Tests 4979
passed, 2 skipped.
- `--ran`: `✓ dispatch-gates --ran: 58 derived famil(ies) accounted for
— 58 run, 0 NOT-MEASURED`.
- Changed lines per the derivation: 4,694 (+2,671 / −2,023, 42 files),
over the 3,000 human-merge threshold.
</details>
<details><summary>Live: before on main, after on the branch (taken on
#30552 head 9914c31; no console source changed since)</summary>
- Before, on `main` `fa9c0aa2`: entering Setup opened the currency
dialog over the page, zh and en.
- After, zh browser (`zh-CN`, `Asia/Shanghai`): `/home` shows the
checklist, progress `已完成 2/6` (the rig's dev seed sets the zone and
seeds members). Opening 默认语言和国家/地区 pre-fills 简体中文 and 中国 (CN). Clearing
the country box and typing 「中国」 leaves `中国 (CN)` in the box; Confirm
sent exactly one write, `PUT /api/settings/localization
{"locale":"zh-CN","default_country":"CN"}`, answered 200, and the row
turned done, progress `已完成 3/6`. `/apps/setup` afterwards: 0 dialogs.
- After, en browser (`en-US`, `America/New_York`): `3 of 6 done`;
opening Default currency pre-fills `USD — US Dollar` and sends no write;
`/apps/setup`: 0 dialogs.
</details>
<details><summary>Ablations (taken on #30552, each through
scripts/ablation-replace.mjs, restored to the HEAD blob with git diff
HEAD empty)</summary>
- `SettingsField.tsx` name match removed:
`SettingsField.domainOptionTypedName-30206` 3 failed, 2 passed (the two
verbatim cases stay green, as they should).
- `settingsFieldRefusals.ts` re-wording disabled: 7 failed across
`settingsFieldRefusals-30206`, `FirstRunChecklist` and
`SettingsView.field-errors`.
- `mayWrite` no longer reads the manifest's `writePermission`:
`FirstRunChecklist` "a member … nothing is drawn" fails, 1 of 10.
- `isKeySet` no longer reads `source`: 7 failed across
`FirstRunChecklist` and `firstRunChecklistLogic`.
</details>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibynkEvahBBwY1bFkmH
<!-- signed: domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp
-->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 555eb1b commit 18870c9
42 files changed
Lines changed: 2671 additions & 2023 deletions
File tree
- .changeset
- docs/qa/platform-checklist
- examples/app-showcase/e2e
- ui
- apps/console/src
- __tests__
- components
- pages
- auth
- settings
- __tests__
- e2e/live
- packages/i18n/src
- __tests__
- locales
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
218 | 219 | | |
219 | 220 | | |
220 | 221 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | 22 | | |
24 | | - | |
| 23 | + | |
25 | 24 | | |
26 | 25 | | |
27 | 26 | | |
| |||
38 | 37 | | |
39 | 38 | | |
40 | 39 | | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | 40 | | |
51 | 41 | | |
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 | | - | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| 45 | + | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| |||
195 | 196 | | |
196 | 197 | | |
197 | 198 | | |
198 | | - | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
199 | 206 | | |
200 | 207 | | |
201 | 208 | | |
202 | 209 | | |
| 210 | + | |
203 | 211 | | |
204 | 212 | | |
205 | 213 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | 21 | | |
23 | 22 | | |
24 | 23 | | |
| |||
528 | 527 | | |
529 | 528 | | |
530 | 529 | | |
531 | | - | |
532 | | - | |
533 | | - | |
534 | | - | |
535 | | - | |
536 | 530 | | |
537 | 531 | | |
538 | 532 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
178 | 178 | | |
179 | 179 | | |
180 | 180 | | |
181 | | - | |
| 181 | + | |
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
148 | | - | |
| 148 | + | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | 39 | | |
43 | 40 | | |
44 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
140 | | - | |
| 140 | + | |
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
44 | | - | |
45 | | - | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| |||
0 commit comments