Repository navigation
Commit f0857d2
fix(ui): KPI tiles sit two across on a phone, the calendar footer counts its loaded dates, and record not-found names no access (#28060)
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgNkgMeS3Ttka5cdjopEKhKg)_
## 维护者速读
- 改了什么:手机上首页的 4 个 KPI 卡片改为每行两张;日历视图底部计数改称「为这些日期加载的 N
条记录」;记录打不开时的提示改为「不存在、可能已删除,或你可能没有访问权限」。三处都是界面层改动,十个语言包同步。
- 为什么改:HotCRM 18.2.0 全量走查里,手机首页的四张 KPI
各占一行占满首屏;日历底部的数字其实只是当前日期窗口里的记录,却和列表的「总数」用同一句话;经理打开无权查看的商机时被告知「不存在或已删除」,而服务端对无权读和真不存在都回
404,界面分不出两者。
- 风险与回滚:KPI 改动作用于所有「裸数字 columns 为 4 及以上」的页面网格在手机宽度(sm
以下)的列数,桌面和平板不变,作者写了响应式对象的网格不变;计数只在日历按日期窗口取数时换文案,表格、看板和内联数据的计数原样;文案改动只改两个键的值。回滚即还原本
PR。
-
先看:`ui/packages/components/src/renderers/layout/grid.tsx`(裸数字的手机降级那段);`ui/packages/plugin-list/src/ListView.tsx`(record-count-bar
里的 calendarWindowed 分支);`ui/packages/i18n/src/locales/en.ts`(新键
`list.calendarRecordCount` 与改值的 `recordNotFoundDescription`)。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/components/src/__tests__/grid-phone-tiles-27355.test.tsx
packages/plugin-list/src/__tests__/ListView.calendarFooterCount-27355.test.tsx
packages/app-shell/src/views/RecordDetailView.recordReadFailure-11902.test.tsx`,预期
3 个文件全绿。
- 需要你做的:无,席位落地。
Fixes #27355
Clause-②: no
Metadata-protocol: none
## What changes
Rows 7, 8 and 11 of #27355, re-measured on `origin/main` at 3f02da1.
Rows 1 and 5 are answered in the Acceptance notes and this PR does not
touch them.
- **Row 11, KPI tiles on a phone.** A layout `grid` with a bare
`columns` count used to ramp to one column below `sm`, so a home page's
KPI strip (`columns: 4` over `object-metric` tiles, the same shape as
the showcase home and the schema catalog's
`report/report-header-with-kpis`) took a row per tile and filled a
phone's first screen. A bare count of four or more now draws two across
below `sm` and the authored count from `md` (`grid grid-cols-2
md:grid-cols-4`). That matches the dashboard's own phone layout, which
already puts metric tiles two across (`DashboardRenderer`'s mobile
branch). A bare 2 or 3 still stacks on a phone and pairs from `sm`, a
bare 1 is one across everywhere, and an authored breakpoint object is
drawn as written, `{ xs: 1, md: 4 }` included.
- **Row 11, the page tab strip.** Already done on `main`: `page:tabs`
scrolls sideways with an edge fade on the side that hides a tab
(`useHorizontalOverflow` + `data-overflow` mask in `containers.tsx`),
landed by #27849 after 18.2.0. Measured: the 18.2.0 release commit's
`containers.tsx` has no `stripRef`, the #27849 merge has it.
- **Row 8, the calendar's footer count.** A calendar hosted by a list
view fetches the date window it draws (`calendarWindowed`), so the
footer's `data.length` is that window's records, not the view's. It read
`N records`, the words the grid uses for the view's total. It now reads
`N records loaded for these dates` (new count family
`list.calendarRecordCount`, every CLDR slot in all ten packs, mirrored
in `LIST_DEFAULT_TRANSLATIONS`). Counting the view instead would need a
second query without the window; the label is what the existing data
path supports. A grid, a board and a calendar over rows handed in whole
keep `N records`.
- **Row 7, the record not-found copy.** `getData` reads the record with
the caller's context and throws `recordNotFoundError` when the read
finds nothing, so a row the reader may not see answers `404
RECORD_NOT_FOUND` exactly like a missing one, and the page cannot tell
them apart without the server revealing existence.
`empty.recordNotFoundDescription` and `detail.recordNotFoundDescription`
now say "The record you are looking for does not exist, may have been
deleted, or you may not have access to it." in all ten packs, and
`DETAIL_DEFAULT_TRANSLATIONS` follows. The record page's inline fallback
(`RecordDetailView.tsx`) follows too. A refused read (403) still shows
the access-denied state, unchanged.
- Changeset: `@object-ui/components`, `@object-ui/plugin-list`,
`@object-ui/plugin-detail`, `@object-ui/i18n` patch.
## Tests
-
`ui/packages/components/src/__tests__/grid-phone-tiles-27355.test.tsx`:
4, 5, 6, 8 and 12 across draw two on a phone and the authored count from
`md`; controls: 1, 2 and 3 across unchanged, an authored breakpoint
object unchanged (three shapes, `{ xs: 1, md: 4 }` among them).
-
`ui/packages/plugin-list/src/__tests__/ListView.calendarFooterCount-27355.test.tsx`:
a windowed calendar counts the 12 records of its month (not the 42 of
the object) as `12 records loaded for these dates`, and one record in
the singular; controls: a grid over the same rows reads `42 records`,
and a calendar over rows handed in whole reads `42 records`.
- Existing pins updated to the new ramp: `grid-breakpoint-columns-7097`,
`grid-flat-columns-retired-11505`, `grid-dom-attribute-whitelist`,
schema-catalog `grid-columns-key` (three `columns: 4` examples), and the
not-found constant in `RecordDetailView.recordReadFailure-11902` (its
403 and failed-load cases still assert no "may have been deleted").
<details><summary>Runs (head 8686ef1 unless named)</summary>
- Targeted set on the final head (`check-i18n-call-site-keys`,
`DetailView`, the five grid pins, schema-catalog `grid-columns-key`,
both calendar footer/window files,
`RecordDetailView.recordReadFailure-11902`,
`RecordDetailView.deleteConfirmDialog-11001`,
`defaults-maps-mirror-en-pack`, all of `packages/i18n/src/__tests__/`):
`Test Files 104 passed (104)`, `Tests 1675 passed | 13 skipped (1688)`.
- Affected packages whole, at 40a769c before the merge of `main`
(`packages/components/`, `packages/i18n/`,
`packages/plugin-list/src/__tests__/`,
`packages/plugin-detail/src/__tests__/`, `examples/schema-catalog/`,
plus the app-shell files above): `Test Files 977 passed | 2 skipped`,
one red (`DetailView` asserting the old sentence), fixed in 40a769c and
green since.
- Ablation 1 (`scripts/ablation-replace.mjs`, anchor `if (baseCols >= 4)
{` replaced by `if (false) {`, i.e. the old ramp; the test resolves
`grid.tsx` from source): `grid-phone-tiles-27355` `Tests 5 failed | 3
passed (8)`, the 5 = every four-or-more pin, the 3 = the controls;
restored blob == HEAD `41b7636b1aa5`, `git diff HEAD` empty.
- Ablation 2 (anchor `? t('list.calendarRecordCount', { count:
data.length })` replaced by the `list.recordCount` call, i.e. the old
words over the same count; `ListView` resolves from source):
`ListView.calendarFooterCount-27355` `Tests 2 failed | 2 passed (4)`,
the 2 = the calendar label pins, the 2 = the grid and inline-data
controls; restored blob == HEAD `8866406e8d92`, `git diff HEAD` empty.
- `pnpm --filter @object-ui/components --filter @object-ui/plugin-list
--filter @object-ui/plugin-detail --filter @object-ui/i18n run
type-check`: exit 0 (each runs `tsc --noEmit && tsc -p
tsconfig.test.json`).
- `pnpm run lint` in `components`, `plugin-list`, `plugin-detail`,
`i18n`: exit 0, 0 errors.
- `node scripts/check-i18n-en-drift.mjs` (ui): exit 0, 2 en values
changed, 18 pack values followed.
</details>
## Acceptance notes
Per-row disposition of #27355 rows 1, 5, 7, 8 and 11, re-measured on
`origin/main` at 3f02da1 (rows 2–4, 6, 9–10 and 12–13 are answered on
the card by other seats):
| Row | Bucket | Reason |
|---|---|---|
| 1 · path steps cannot be clicked | CARD (spec, then ui) |
`record:path` is a readout by design: its header comment records that it
carries no handler and that click-to-advance is a separate feature, and
its only channel, the record context, exposes no record-field write. The
server side already exists (an update is judged by the object's
`state_machine` validation rule, ADR-0020), so the card is a spec opt-in
on `RecordPathProps` plus a write channel and confirm in the renderer.
Filed by the seat as #28061. |
| 5 · the record page has no Delete | ALREADY DONE (generic path) | The
record header's `⋯` menu ends with Delete (`sys_delete` in
`RecordDetailView`'s synthesized system actions, present in the 18.2.0
release commit too). It is gated by the same `delete` affordance row
(`recordDelete` and `rowDelete` are both `crud: delete, grant: delete`
in `affordanceGrants.ts`) and the same per-record verdict as the list
row menu, so a reader offered Delete in the list row is offered it in
the header. `RecordDetailView.deleteConfirmDialog-11001` passes on this
head. If HotCRM can name an object, role and page where the header lacks
it, that is a new measurement; |
| 7 · "does not exist or has been deleted" for an unreadable record |
TAKE | This PR (copy names both). |
| 8 · calendar footer counts the loaded window | TAKE | This PR (label
names the window). |
| 11 · KPI tiles a row each; tab strip overflows | TAKE (tiles) /
ALREADY DONE (tab strip) | This PR for the tiles; the strip by #27849. |
`RecordDetailView.tsx` changes by one line: the inline `defaultValue` of
`empty.recordNotFoundDescription` follows the new `en` sentence, which
`check-i18n-call-site-keys` (default-value drift) requires. No other
line of that file is touched.
## Verification
Local gate subset derived by `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` on this diff, each
recorded with its exit code before any pipe and reconciled with `--ran`;
every family CI runs is read from this PR's check-runs.
<details><summary>Gate run (head 8686ef1)</summary>
- `dispatch-gates --commands`: 34 commands, every one `:: exit 0`, among
them `node scripts/check-sdui-manifest.mjs` and its `--self-test`, `pnpm
check:type-check-coverage`, `pnpm check:type-check-debt`, `pnpm
check:nul-bytes` and `pnpm --dir ui exec vitest run scripts/` (`Test
Files 169 passed | 2 skipped (171)`).
- `dispatch-gates --ran`: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.
- Outside the derivation: `pnpm --dir ui run check:new-line-citations`
exit 0 (0 new citations).
- CI check-runs: read by the seat.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CXNi3EtSwLHRSornGu)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ad030d8 commit f0857d2
23 files changed
Lines changed: 343 additions & 39 deletions
File tree
- .changeset
- ui
- examples/schema-catalog/test
- packages
- app-shell/src/views
- components/src
- __tests__
- renderers/layout
- i18n/src/locales
- plugin-detail/src
- __tests__
- plugin-list/src
- __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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
129 | 129 | | |
130 | 130 | | |
131 | 131 | | |
132 | | - | |
| 132 | + | |
| 133 | + | |
133 | 134 | | |
134 | 135 | | |
135 | | - | |
| 136 | + | |
136 | 137 | | |
137 | 138 | | |
138 | 139 | | |
139 | 140 | | |
140 | | - | |
| 141 | + | |
141 | 142 | | |
142 | 143 | | |
143 | 144 | | |
144 | 145 | | |
145 | | - | |
| 146 | + | |
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
131 | | - | |
| 131 | + | |
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3643 | 3643 | | |
3644 | 3644 | | |
3645 | 3645 | | |
3646 | | - | |
| 3646 | + | |
3647 | 3647 | | |
3648 | 3648 | | |
3649 | 3649 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
171 | | - | |
| 171 | + | |
| 172 | + | |
172 | 173 | | |
173 | | - | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
174 | 178 | | |
175 | 179 | | |
176 | 180 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
161 | | - | |
162 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
163 | 163 | | |
164 | | - | |
| 164 | + | |
165 | 165 | | |
166 | 166 | | |
167 | 167 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
167 | | - | |
168 | | - | |
| 167 | + | |
| 168 | + | |
169 | 169 | | |
170 | 170 | | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
122 | 127 | | |
123 | 128 | | |
124 | | - | |
125 | | - | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
126 | 135 | | |
127 | 136 | | |
128 | 137 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
890 | 890 | | |
891 | 891 | | |
892 | 892 | | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
893 | 902 | | |
894 | 903 | | |
895 | 904 | | |
| |||
1272 | 1281 | | |
1273 | 1282 | | |
1274 | 1283 | | |
1275 | | - | |
| 1284 | + | |
1276 | 1285 | | |
1277 | 1286 | | |
1278 | 1287 | | |
| |||
3453 | 3462 | | |
3454 | 3463 | | |
3455 | 3464 | | |
3456 | | - | |
| 3465 | + | |
3457 | 3466 | | |
3458 | 3467 | | |
3459 | 3468 | | |
| |||
0 commit comments