From cfed06822f72ac9c4ced7568ff5c04b9939362f3 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Tue, 29 Sep 2026 00:43:43 +0800 Subject: [PATCH] feat(reports): drop the report centre intro; docs for self edit and account menu (S3+S4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「我的报告」 starts at 报告记录: the supporting paragraph and the overview line are gone. CHANGELOG, DESIGN, the real-device checklist and PROGRESS for TASK-self-edit-avatar-menu-20260928. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE --- CHANGELOG.md | 7 ++ ...PROGRESS-self-edit-avatar-menu-20260928.md | 106 ++++++++++++++++++ docs/tasks/README.md | 2 +- .../testing/self-edit-avatar-menu-20260928.md | 29 +++++ frontend/DESIGN.md | 4 +- frontend/src/app/globals.css | 26 +---- .../personal-report-center.tsx | 16 +-- frontend/tests/personal-report-entry.test.ts | 12 +- 8 files changed, 155 insertions(+), 47 deletions(-) create mode 100644 docs/tasks/PROGRESS-self-edit-avatar-menu-20260928.md create mode 100644 docs/testing/self-edit-avatar-menu-20260928.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5999c070..43a6baf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 印度占星 Skill 更新日志 +## 2026-09-28 — 星盘档案能改自己的资料、每页点头像都弹账户菜单、我的报告去掉顶部说明(待验收) + +- 星盘档案里「本人」也有「编辑」了:和家人同一张表,能改称呼、出生日期与时间、出生地、岁差和性别;本人仍然不能删除。此前全站找不到改自己出生资料的地方(BUG-1081)。改完后首页、星盘页、星历按新资料重读。 +- 星盘、星历、我的报告、星盘档案这几页,点左下角头像不再直接回首页,而是弹出和首页一样的账户菜单。切换主题当场生效;点「个人资料」「通用设置」「账户与点数」「退出登录」会回到首页并直接打开对应的窗口,刷新或返回不会再弹一次。 +- 「我的报告」顶部那段「有填报到分钟的出生时间即可生成……」说明和「共 N 份 · 已完成 N 份」一行去掉,页面直接从「报告记录」开始。 +- Skill 版本不 bump(Skill 文本未改)。不改数据库结构。 + ## 2026-09-28 — 「那一刻的天空」第三轮:只放图、印出生日期时刻城市、过场放慢、兑换弹窗改时机(待验收) - 星盘页点「那一刻的天空」:去掉灰色面板和标题栏,压暗的页面上只放这张图,按屏幕大小完整显示;图下一个「保存图片」,右上角一个小 ×,点图外或按 Esc 也能关。 diff --git a/docs/tasks/PROGRESS-self-edit-avatar-menu-20260928.md b/docs/tasks/PROGRESS-self-edit-avatar-menu-20260928.md new file mode 100644 index 00000000..a39d5235 --- /dev/null +++ b/docs/tasks/PROGRESS-self-edit-avatar-menu-20260928.md @@ -0,0 +1,106 @@ +# PROGRESS · 星盘档案本人编辑、各页头像菜单、我的报告去说明(2026-09-28) + +任务书:`docs/tasks/TASK-self-edit-avatar-menu-20260928.md`。产品授权直接执行(Claude 子代理)。 + +- 基线:`origin/staging` = `adbb2054`(birth-sky-polish 已推 staging 后开工,串行) +- 分支 / 工作树:`codex/self-edit-avatar-menu-20260928` / `.worktrees/self-edit-avatar-menu-20260928`(三个本地提交,**未推送**) +- 环境:Node v22.14.0(`/exec-daemon/node`);系统 `python3`;无 Docker;无头 Chrome(本机 `/dev/shm` 只有 64MB,需 `--disable-dev-shm-usage`) +- BUG 编号:开工时最大 BUG-1080;本单 BUG-1081 + +## 结论 + +| 项 | 结果 | 提交 | +| --- | --- | --- | +| S1 本人可在星盘档案编辑(BUG-1081) | 完成 | 1 | +| S2 各页头像弹账户菜单 | 完成 | 2 | +| S3 我的报告去掉顶部说明区 | 完成 | 3 | +| S4 文档(DESIGN、VOICE、CHANGELOG、BUG_HISTORY、真机清单、索引、本文件) | 完成(DESIGN / VOICE 随 S1、S2 各自提交,见偏离 1) | 1–3 | +| 让步 | 未砍:只读模式余额照常显示;弹窗在首页 `hydrated && account` 后打开 | — | + +## S1 · 本人可编辑(BUG-1081) + +- `lib/self-profile-save.ts`:`selfProfilePatchBody`(原 `persistProfile` 的载荷,带 `birth_time_source`,**不带** `gender`)、`saveSelfProfile`(`PATCH /api/account`,返回应用服务端出生时间真值后的资料与 `chartBirth.fingerprint`)、`saveSelfGender`(单字段 `{ gender }`)。 +- 首页 `use-profile-onboarding.ts` 的 `persistProfile` 改用 `saveSelfProfile`(行为不变)。无 UI 调用的 `saveProfile` 删除;它独用的 hook 参数 `setBirthTimeConsultationConsent`、`setRectificationError` 从 hook 类型与 `page.tsx` 调用处一并移除(`page.tsx` 里这两个 state 仍被别处使用)。 +- `people-page.tsx`:本人详情底部「本人不能删除」下面加「编辑」;编辑视图与他人同一张 `ChartProfileForm`,标题「编辑我的资料」、说明「称呼和出生资料。」,无删除区。`saveSelf`:出生资料 `saveSelfProfile(draft)`,性别变了再 `saveSelfGender`;然后 `pinChartSnapshotIdentity`(星盘缓存按新指纹失效)、`clearHomeWarmSnapshot`(首页暖快照与今日卡)、`invalidateEphemerisPage`、布局 provider `setAccount`(资料与 `chartBirth.fingerprint`)、`invalidateSubjectCatalog` 后重读列表。失败时表单里显示服务端文案,没有就「保存失败,请重试」。 +- 编辑他人后原来只 `invalidateSubjectCatalog` + 重读(已核实);本人多出的几项是因为本人资料还活在布局 provider、首页暖快照和星盘 / 星历缓存里。 +- 删掉详情「性别」下的小字「在「设置 → 个人资料」里改」和它的 CSS 规则 `.people-archive-fact-note`。 +- `.people-archive-foot` 加 `gap`、`justify-items: center`(灰字与按钮上下排)。 + +## S2 · 各页头像弹账户菜单 + +- `lib/account-dialog-intent.ts`:`/?account=profile|general|billing|logout` 的生成与解析,其他值一律忽略。 +- `app-sidebar.tsx`:页脚在两种模式都是同一个 `Menu.Root`(全文件只有一个)。首页由 `controls` 控制开合,四项 `Menu.Item` 就地开弹窗;其他页菜单自持开合状态(`useState`,在 `AppSidebar` 里,不在 `Home()`),四项是 `Menu.LinkItem` + `AppLink` 到 `/?account=`,点了收起手机抽屉。主题菜单不变、就地生效。余额「N 点」来自布局 provider 的账户,只读模式照常显示。账户还没读到时页脚是同尺寸的静态块(无链接、无转圈);未登录仍是「去登录」。 +- `people-home-links.tsx` 新增 `AccountDialogDeepLink`:挂载时读一次 `?account=`、立刻 `replaceState` 去掉(非法值也去掉),`ready` 后调用 `openAccountDialog`(billing 带 `source: "account-menu"`,与首页菜单一致),之后不再触发。`page.tsx` 只多一行接线(import 合在同一行)。 +- `page.tsx`:1364 → 1363 行;`Home()` 的 `useState` / `useRef` 数不变(未新增)。 + +## S3 · 我的报告 + +- 删 `.report-center-hero` 整段(说明段 + 统计行)与只给它用的 `latestReady`(`useMemo` import 随之移除);`hasGenerating` 仍给轮询用,保留。`globals.css` 删 `.report-center-hero*`、`.report-center-meta` 与移动端那条规则。生成按钮的 `title` 不动。 + +## 截图(本地宿主页,虚构资料,交付构建的编译 CSS) + +- `/tmp/claude-1000/-workspace-Jyotisha/fb88e01e-1ced-4bcc-92fe-44c5bdcc878e/scratchpad/selfedit/people-menu-1280x800.png`(/people 本人详情,底部「本人不能删除」+「编辑」;侧栏账户菜单展开) +- 同目录 `people-menu-390x844.png`(手机抽屉里展开账户菜单) +- 两张图 CDP 读回:菜单四个弹窗项的 href 依次为 `/?account=profile|general|billing|logout`,无横向滚动。宿主页用 esbuild 打包真实组件,`next/navigation`、`next/link` 用本地 shim(未提交)。 + +## 验证 + +| 项 | 基线 `adbb2054` | 交付(第 3 个提交) | +| --- | --- | --- | +| `tsc --noEmit` | 0 错 | 0 错 | +| `npm run lint` | 0 error / 127 warning(polish 验收记录) | 0 error / 126 warning | +| `npm test` | tests 4322 · pass 4267 · fail 24 · cancelled 0 · skipped 31 | tests 4334 · pass 4279 · fail 24 · cancelled 0 · skipped 31 | +| 失败清单 | 24 条(Docker / PostgreSQL / 部署套件) | 与基线逐条同名 | +| 测试名对比(`frontend/AGENTS.md` 方法) | — | 消失 1(改名,见下表)、新增 13 | +| `python3 -m pytest tests/test_repo_privacy_markers.py` | — | 63 passed | +| `rm -rf .next && next build` | `/` ○ Static | `/` ○ Static;路由表与基线逐行相同 | +| `page.tsx` 行数 / `Home()` state·ref 数 | 1364 / — | 1363 / 不变 | + +未跑 Python 快速门:本单没有改 `scripts/**` 或 `tests/**`(Python)。 + +### 首屏 gzip(`index.html` 引用的 `/_next/static/*.js|css` 逐个 gzip -9 求和;基线在临时 detached 工作树同机同脚本干净构建) + +| | 基线 `adbb2054` | 交付 | 变化 | +| --- | ---: | ---: | ---: | +| JS | 650,972 B | 651,520 B | +548 B(+0.08%) | +| CSS | 66,126 B | 66,031 B | −95 B(−0.14%) | + +JS 增量是首页 bundle 里的 `AccountDialogDeepLink`、`account-dialog-intent` 与侧栏只读菜单分支;CSS 减少是删掉的报告中心说明区与 `.people-archive-fact-note`。 + +## 改动的既有断言(原值 / 新值 / 原因,测试文件里同样写了注释) + +| 测试 | 原值 | 新值 | 原因 | +| --- | --- | --- | --- | +| `sidebar-contract` · read-only when `/` is not mounting it | 只读页脚是 `` | 不再有页脚链接;全文件一个 `Menu.Root`,两种模式都渲染;只读弹窗项是 `AppLink` 到 `accountDialogHref(dialog)` | S2 产品决定 | +| `home-warm-return-lifecycle` · warm return to bare / … | `followLink("林遥,打开账户")` | 断言页脚不再是该链接;用等价站内跳转 `followHref("/")`(记下目标再路由,与 `AppLink` 同步骤) | S2;测试守的是「暖回裸 `/` 落空白首页」 | +| `new-chat-from-people-lifecycle` · stashed rectification … not reopened | 等并点只读页脚链接回 `/` | 等页脚菜单按钮出现、断言无该链接,`followHref("/")` 回 `/` | 同上 | +| `new-chat-from-people-lifecycle` 测试工具 `button(label)` | 取第一个文本含 label 的按钮 | 跳过 `.profile-trigger`(页脚菜单按钮也含本人名字) | S2 后页脚是按钮;不改断言,只避免点错元素 | +| `profile-gender-ui` · detail card(**改名**:`…本人 points to 设置 → 个人资料` → `…本人 edits it in the same form as everyone`) | 本人 `
未填在「设置 → 个人资料」里改
`,本页无本人性别单选 | 本人 `
未填
`;详情卡无单选,本人编辑视图有 | BUG-1081 | +| `profile-gender-ui` · CSS 类清单 | 含 `people-archive-fact-note` | 去掉(类与规则已删) | BUG-1081 | +| `settings-mvp-contract` · chart library owns self-profile editing | `onSubmit={(event) => void saveOther(event)}` | `editingSelf ? saveSelf(event) : saveOther(event)`,且 `await saveSelfProfile(draft)` | BUG-1081 | +| `people-archive-regressions` · people delete … | `title={selectedId ? "编辑人物" : "添加一个人"}` | 前面多一档 `editingSelf ? "编辑我的资料"` | BUG-1081 | +| `consultation-entrypoint` · profile and place saves do not auto-start … | 切出 `saveProfile` 断言不评估 | 断言首页无 `saveProfile`;切出 `/people` 的 `saveSelf` 断言不评估 | `saveProfile` 删除 | +| `profile-onboarding-birth-sky-reveal` · only own onboarding place save triggers | 切出 `saveProfile` 断言不含 `onOwnBirthPlaceSaved` | 断言首页无 `saveProfile`(people-page 不触发由下方原循环锁住) | 同上 | +| `birth-time-consultation-consent` · source-aware consultation options | `match(page, /birthTimeConsultationOptionsCopy\(savedProfile\)/)` | `doesNotMatch`;录入表单那条断言不动 | 这句只在已删的 `saveProfile` 保存提示里 | +| `birth-time-guided-review-fixes` · terminal CJK copy … | `match` 「出生资料已保存。${…(savedProfile)}」 | `doesNotMatch`;其余断言不动 | 同上 | +| `personal-report-entry` · global report copy … | 报告中心源码四条 `match`(分钟即可生成 / 需要具体分钟 / 与某一次对话无关 / 后台继续处理) | 四条 `doesNotMatch`,另断言无 `report-center-hero` / `report-center-meta` / 报告概览;按钮 `title` 断言不动 | S3 | + +测试工具改动(非断言):`tests/home-surface.ts` 的首页源码清单加 `src/lib/self-profile-save.ts`(`persistProfile` 的载荷与 `applyPersistedBirthTime` 行搬到这里,`profile-persistence` 的原断言继续命中);两份生命周期测试新增 `followHref` 帮手。 + +## 偏离任务书 + +1. **DESIGN / VOICE 随各自的 S1、S2 提交**,不全压在 S3+S4:AGENTS §7.5 要求改 UI 的提交同时更新 DESIGN。S4 提交里是 S3 的 DESIGN、CHANGELOG、真机清单、索引与本文件。 +2. **首页 `saveProfile` 连带删掉两个 hook 参数**(`setBirthTimeConsultationConsent`、`setRectificationError`),它们只被 `saveProfile` 用;否则会留下未使用的参数。 +3. **本人保存成功后不再显示「出生资料已保存。…」提示**:那句只在已无 UI 的 `saveProfile` 里;星盘档案保存后直接回到已更新的详情。若产品想要一句确认,另开单(详情卡现有的 notice 是错误样式,不宜复用)。 +4. **本人编辑视图说明写「称呼和出生资料。」**,没写他人那句「填报的出生时间保存后不能再改」——本人资料走账户接口,可以再改。 + +## 另发现(不在本单范围,未改) + +- **封面城市取不到**:库里中国地点标签以「中国 · 」开头(`lib/geoapify-location-service.ts` 生成 `中国 · 省 · 市 · 区`),而 polish 单的 `lib/birth-sky/caption.ts` `birthSkyCity` 要求第一段是省级名,遇到「中国」直接返回 null。所以中国出生地的封面实际只会印到时刻、不带城市。该代码已在 staging(`adbb2054`)。 +- 账户接口在 `birth_time_status = confirmed` 时,声明字段变化不会重置状态(`resolveAccountBirthTimeApplicationPatch` 直接返回 `{}`),这是既有语义,本单未动;本人在星盘档案改时间后若原来是 confirmed,排盘仍按 confirmed 的时间。建议产品确认是否符合预期。 + +## 环境缺口 + +- 无登录态与真机:本人编辑的真实保存、跳首页开弹窗、手机抽屉行为只在单测、源码合同与本地宿主页截图上验证;真机清单 `docs/testing/self-edit-avatar-menu-20260928.md`(17 条)。 +- 无 Docker:24 条 DB / 部署套件失败与基线逐条同名。 +- 未推送、未部署。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 32153fc6..b19d8c74 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -251,7 +251,7 @@ | `TASK-birth-sky-cover-20260928.md` | `PROGRESS-birth-sky-cover-20260928.md` | **「那一刻的天空」封面**:出生那一刻头顶的真实天空做成 3:4 封面(产品从 A 年轮 / B 一笔画 / C 天空三版样稿中选 C,样稿 `assets/birth-sky-cover-sample-20260928.html`);星盘页按钮 + 保存图片、报告封面(须对得上报告出生资料)、首次建盘揭幕;不印出生资料、句库只描述天空、无新依赖 | 已验收(Claude 2026-09-28),随分支五个提交推 staging;T5 由追加单解除 | 新功能,不开 BUG | | `TASK-birth-sky-followup-20260928.md` | `PROGRESS-birth-sky-cover-20260928.md`(追加单) | **天空封面追加**:报告封面按生成时出生资料指纹绑定(方案 A,旧报告永不显示);首次建盘揭幕改群星汇聚过场(无按钮、3 秒预算、可点跳过、减弱动态只淡入淡出) | 已验收(Claude 2026-09-28),已推 staging | 新功能,不开 BUG | | `TASK-birth-sky-polish-20260928.md` | `PROGRESS-birth-sky-polish-20260928.md` | **天空封面第三轮**:星盘页弹层只留图;封面印「日期 时刻 · 城市」(推翻「不印出生资料」);过场放慢到约 6.5 秒;兑换弹窗改到首条付费消息被拒(402)时再弹;BUG-1080 过场被兑换弹窗挡住 | 已实现待验收(分支 `codex/birth-sky-pacing-20260928`,未推送);BUG-1080 investigating(Chrome 下「弹窗盖住过场」已排除,修复不依赖根因) | BUG-1080 | -| `TASK-self-edit-avatar-menu-20260928.md` | `PROGRESS-self-edit-avatar-menu-20260928.md` | **本人可编辑 + 各页头像菜单 + 报告页去说明**:`/people` 本人可编辑出生资料(BUG-1081,`ab6c55f8` 漏掉);次级页头像弹同一账户菜单、弹窗项跳 `/?account=`;我的报告删顶部说明与统计 | 待领取(须等 birth-sky-polish 推 staging 后串行) | BUG-1081 起 | +| `TASK-self-edit-avatar-menu-20260928.md` | `PROGRESS-self-edit-avatar-menu-20260928.md` | **本人可编辑 + 各页头像菜单 + 报告页去说明**:`/people` 本人可编辑出生资料(BUG-1081,`ab6c55f8` 漏掉);次级页头像弹同一账户菜单、弹窗项跳 `/?account=`;我的报告删顶部说明与统计 | 已实现待验收(分支 `codex/self-edit-avatar-menu-20260928`,未推送) | BUG-1081 | | `TASK-serif-headings-20260928.md` | `PROGRESS-serif-headings-20260928.md` | **全站标题改用自托管宋体、正文保持黑体**:产品推翻 BUG-737「CJK 不用衬线」结论(保留「声明的字体必须可加载」「不落系统宋体」两条);Noto Serif SC SemiBold 按通用规范汉字表 6500 字 unicode-range 切片自托管(改名 Jyotisha Serif SC),swap 不 preload;首页宋体流量 ≤300 KB;先于天空封面单 | 已实现待验收(分支 `codex/serif-headings-20260928`,未推送) | 不开新 BUG;BUG-737 追加说明 | | `TASK-cend-ui-claude-alignment-20260916.md` | `PROGRESS-cend-ui-r1/r2/r3-20260916.md` | **C 端界面向 claude.ai 产品界面对齐(三轮串行 R1→R2→R3,都动 `globals.css`,不得并行)**:根因是 `frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,它自己在 Known Gaps 里写明 claude.ai 产品界面不在范围内,而 `DESIGN.md:3` 把它当成了产品界面的实现契约。**R1**:`--font-display` 里 Tiempos Headline / StyreneB **从未加载**(无 `@font-face`、`public/` 无字体、`layout.tsx` 只 vendor 了 Inter),中文标题全站落到 **宋体 / SimSun**,波及 20 处含助手回答的 h2/h3(BUG-737);亮色强调色 `#85432f` 与暗色 `#d78064` 不同源,产品拍板亮色换 **Claude coral `#cc785c`**,**易漏点**是 `globals.css:16` 的 `--color-ring` 硬编码在 `@theme inline` 里不跟随 `:root`,另有第四个 `:root` 亮色块(`:4358`)必须同步(BUG-738);`.composer-footer` 常驻 44px + 顶栏 68px + `--composer-reserve` 148px,每屏固定吃掉 216px,模型选择器移进输入框内部、删掉底栏、顶栏收到 46px 并删「分析对象」副标题。**R2**:空状态是营销落地页(hero 卡 + 两张 132px 入口大卡 + 3 列 156px 主题卡),输入框被压在 **800px 以上**内容之下,重排成「问候 + 居中输入框 + 两枚入口 pill + 一排 chip」。**R3**:侧栏两个 `
` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737–738 | 待领取 | — | | `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-20260916.md`、`PROGRESS-cend-rectification-20260916.md`、`PROGRESS-cend-chart-eph-20260916.md` | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | **R4–R8 全部已实现并验收合入** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置。R6:报告中心卡片网格改行式列表、阅读页并入外壳并把目录挪到右侧常驻。**任务书 E10 过期**——目录在 `cfcd369d` 就已存在,本轮是挪位置定稿而非从零加。挂外壳带出一个真实打印风险已处理:`.chat-app`/`.chat-panel` 是 `height:100%;overflow:hidden`,裸 `window.print()` 会把九节报告裁成一页,阅读页因此多挂一条只在挂载期生效的 print 样式解锁外壳。「生成中的分节进度」做不了——`REPORT_LIST_COLUMNS` 不返回节数,按 VOICE.md 不许前端编。R7:区间常驻条与盘面折叠收敛。**任务书 E9 也不准确**——对话区顶部早有常驻条 `RectificationTimeline` 且窄屏可见,真正只在盘面标题行的是**代表分钟**;因此没另造第二条,在既有条上补齐代表分钟与已答题数(与盘面同一次 `workingRectificationTime()` 调用)。折叠块实际是 **8 个**不是 5 个。**触发让步顺序第 5 条**:收窄进度未做——服务端无该字段,且 `candidate_range` 会放宽(BUG-572),前端相减会把一次放宽报成收窄,已写进 `BLOCKED.md`。顺带修掉一个**静默失效的旧断言**(`slice(indexOf(A), indexOf(B))` 在 B 改名后变成几乎整份文件,四条 `doesNotMatch` 假通过)| diff --git a/docs/testing/self-edit-avatar-menu-20260928.md b/docs/testing/self-edit-avatar-menu-20260928.md new file mode 100644 index 00000000..3c541aae --- /dev/null +++ b/docs/testing/self-edit-avatar-menu-20260928.md @@ -0,0 +1,29 @@ +# 真机清单 · 星盘档案本人编辑、各页头像菜单、我的报告去说明(2026-09-28) + +任务书:`docs/tasks/TASK-self-edit-avatar-menu-20260928.md`。自动化只覆盖了单元测试、源码合同与本地无头 Chrome 样张;下面这些需要登录态在 staging 真机上走一遍。只用自己的测试账号,不要截取真实出生资料发到任何地方。 + +## 星盘档案 · 本人可编辑(BUG-1081) + +1. 打开「星盘档案」,点「本人」那一行:详情底部是灰字「本人不能删除」,下面有「编辑」。 +2. 点「编辑」:标题「编辑我的资料」,表单里有称呼、出生日期与时间、出生地、岁差、性别;底部没有「删除这个人」。 +3. 把出生时间改动几分钟(或改出生地),点「保存」:回到详情,填报时间 / 出生地已是新值。 +4. 从侧栏回首页,再打开「星盘」:星盘按新时间重排(上升或行星宫位跟着变)。 +5. 回到「星盘档案」再把时间改回原值并保存,确认同样生效。 +6. 只改性别(女 / 男 / 不填)并保存:详情「性别」一行跟着变;出生资料不受影响。 +7. 故意清空出生地再保存:「保存」按钮不可点(资料不完整时不提交)。 +8. 家人的「编辑」与「删除这个人」照旧可用。 + +## 各页头像弹账户菜单 + +9. 在「星盘」「星历」「我的报告」「星盘档案」四页各点一次左下角头像:都弹出账户菜单(头像、名字、邮箱、个人资料、通用设置、账户与点数、主题、退出登录),不会直接跳走。 +10. 在任一次级页用菜单里的主题切换浅色 / 深色:当场生效,页面不跳。 +11. 在「星盘」页点「账户与点数」:回到首页,点数窗口自动打开;关掉后刷新页面,不会再弹。 +12. 在「我的报告」点「个人资料」、在「星历」点「通用设置」:各自回首页并打开对应窗口。 +13. 在「星盘档案」点「退出登录」:回首页弹出退出确认。 +14. 手机上(侧栏抽屉打开时)点头像再点「账户与点数」:抽屉收起,回首页打开点数窗口。 +15. 侧栏折叠时(桌面)点头像:菜单在右侧弹出。 + +## 我的报告 + +16. 打开「我的报告」:顶部没有「有填报到分钟的出生时间即可生成……」那段说明,也没有「共 N 份 · 已完成 N 份」一行,直接是「报告记录」。 +17. 右上角「生成完整报告」照常可用。 diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md index 2b283dee..d53cc610 100644 --- a/frontend/DESIGN.md +++ b/frontend/DESIGN.md @@ -561,7 +561,7 @@ one skeleton, none of them carrying the sidebar. ### Personal report centre -- **Structure:** inside the app shell, not a page of its own. The name 「我的报告」 sits in the 46px header with 「生成完整报告」 as the header's one action; the body is supporting copy, the overview line, then the **row list** of reports. +- **Structure:** inside the app shell, not a page of its own. The name 「我的报告」 sits in the 46px header with 「生成完整报告」 as the header's one action; the body starts straight at 「报告记录」 and the **row list** of reports. The supporting paragraph (「有填报到分钟的出生时间即可生成……」) and the 「共 N 份 · 已完成 N 份」 overview line above it were removed on 2026-09-28 (TASK-self-edit-avatar-menu-20260928 S3, product: 「这里的提示去掉」); the minute requirement still lives in the 生成 button's hover title. It used to be a standalone full-screen route with a `report-center-shell` root, a `report-center-topbar` holding one 「返回对话」 link, and a `report-center-hero` with a page-sized h1 — the sidebar vanished the moment you opened it, and the only way back was that link. - **Row list, not a card grid:** `.report-center-list` is one column of `.report-center-row`, hairline-separated by a 1px grid gap over a `--color-border` ground so each boundary is a single rule rather than two touching borders. A card grid costs one scan per card; past five or six reports the reader is looking for state, and a single column puts every status chip on the same x. D12. - **Row anatomy:** status chip, then the title at `--type-title-md`/500, then the meta line (creation time · depth · themes) at `--type-caption` with `tabular-nums`, then — for a ready report — its stored card summary, or for a failed one the failure reason. Actions sit right-aligned on the same row and wrap under the body at the global 767px cut. @@ -697,7 +697,7 @@ page. Three parts now, in reading order: - **Pane menu states:** default is transparent with secondary ink; hover is a 55% wash of `--color-canvas-muted` keeping secondary ink; current is the solid muted surface with primary ink. No accent bar, no trailing navigation chevron, and no weight change — hierarchy here comes from ink rank and surface, matching “Hierarchy inside the nav comes from ink rank, not hue”. The sidebar's 2px `--sidebar-ring` on the active session is deliberately **not** changed to match; the two surfaces read differently on purpose until that is revisited. - **Content width:** the 880px frame now gives the content pane a deliberate inset instead of leaving controls against the divider. Form panes (个人资料, 通用设置) cap their children at 560px and stay left aligned (`.settings-dialog-content--form > *`); list panes (星盘资料, 账户与点数) stay full-bleed within the inset so tables and card grids keep their columns. The cap sits on the children of the scroll container only — never on `.settings-modal` or `.account-settings-shell` — so it cannot make the frame resize between panes (BUG-554 / BUG-698). - **Personal profile:** one row of avatar editing (56px preview, eight palettes, 换一个形象) plus nickname and login email. Below them sits **性别(选填)** (2026-09-27, TASK-consult-gender-optional): three 44px choices 女 / 男 / 不填 in one row (the birth-time range chip look: `--color-border`, selected `--color-action` border + `--color-action-soft`), then one caption line 「用于婚恋解读里判断夫星 / 妻星,不填也能用。」. A choice saves at once (like the avatar palette) and shows 「已保存。」; the choices are disabled while saving, no spinner. This is the only place the owner sets it; onboarding never asks. The header uses the product-level “设置” eyebrow above the current pane title; there is no duplicate 管理星盘资料 button. -- **Chart archive (`/people`):** a static secondary page beside 星盘 / 星历 / 我的报告. Self is the first row and cannot be deleted. Each other row is a name plus birth facts, with no relationship field. Detail shows the reported time and the chart time separately, then place and ayanamsa. Shortcuts are 和 TA 对话, 看星盘, 生成报告, and 和我合盘. Adding a sixth person is disabled with 「最多保存 5 个人的星盘」. Delete confirms with the session and report counts before the server removes them together. There is no 「设为默认」 and no 「当前默认」 badge. +- **Chart archive (`/people`):** a static secondary page beside 星盘 / 星历 / 我的报告. Self is the first row and cannot be deleted, but is edited here like everyone else (2026-09-28, BUG-1081). Each other row is a name plus birth facts, with no relationship field. Detail shows the reported time and the chart time separately, then place and ayanamsa. Shortcuts are 和 TA 对话, 看星盘, 生成报告, and 和我合盘. Adding a sixth person is disabled with 「最多保存 5 个人的星盘」. Delete confirms with the session and report counts before the server removes them together. There is no 「设为默认」 and no 「当前默认」 badge. - **Subject switcher:** the same control sits beside the title on chat, chart, ephemeris, reports, and the archive. It is not in the sidebar header. Choosing someone in a chat that already has messages switches the current person and opens an empty chat. An empty chat rebinds in place. The list shows at most five people. - **States:** open, pane switch, list / self / other / add, saving, success, and error. diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 690c74e1..5837520c 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -3738,8 +3738,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .payment-qr-badge { position: absolute; top: 50%; left: 50%; display: grid; width: 44px; height: 44px; padding: 4px; transform: translate(-50%, -50%); border: 4px solid #fff; border-radius: 12px; background: #fff; box-shadow: 0 2px 10px rgb(0 0 0 / 18%); } .payment-qr-badge svg { display: block; width: 100%; height: 100%; } -/* Global report centre — quiet archive, independent from chat sessions. */.report-center-hero, -.report-center-section{ +/* Global report centre — quiet archive, independent from chat sessions. */.report-center-section{ width: min(900px, 100%); margin-inline: auto; }.report-center-section-heading button:focus-visible, @@ -3750,28 +3749,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class }.report-center-section-heading button svg, .personal-report-back svg, .personal-report-action-end svg{ width: 16px; height: 16px; } -.report-center-hero { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: end; - gap: var(--space-8); - padding: var(--space-10) 0 var(--space-8); -} -.report-center-hero > div { max-width: 760px; } -.report-center-hero > div > p:not(.report-center-meta) { - max-width: 660px; - margin: var(--space-4) 0 0; - color: var(--color-ink-secondary); - font-size: var(--type-body-md); - line-height: 1.65; - text-wrap: pretty; -} -.report-center-meta { - margin: var(--space-4) 0 0; - color: var(--color-ink-tertiary); - font-size: var(--type-caption); - line-height: 1.4; -} .report-center-section-heading { display: flex; align-items: center; @@ -3951,7 +3928,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .report-center-message p { margin: 0; color: var(--color-ink-secondary); font-size: var(--type-body-sm); } @media (max-width: 767px) { - .report-center-hero { grid-template-columns: 1fr; align-items: start; gap: var(--space-6); padding-block: var(--space-8) var(--space-6); } .report-center-row { grid-template-columns: 1fr; gap: var(--space-3); } .report-center-row-actions { justify-content: flex-start; justify-self: start; } .report-center-failure { justify-self: start; } diff --git a/frontend/src/components/personal-report/personal-report-center.tsx b/frontend/src/components/personal-report/personal-report-center.tsx index d27e7b5a..60c7a6c8 100644 --- a/frontend/src/components/personal-report/personal-report-center.tsx +++ b/frontend/src/components/personal-report/personal-report-center.tsx @@ -3,7 +3,7 @@ import { AppLink } from "@/components/app-link"; import { Clock3, FileText, RefreshCw } from "lucide-react"; import { InlineSpinner } from "@/components/inline-spinner"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { SecondaryPageShell } from "@/components/secondary-page-shell"; import { readCurrentSubjectScope, useCurrentSubjectScope, useCurrentSubjectReady } from "@/lib/current-subject"; @@ -127,11 +127,6 @@ export function PersonalReportCenter() { onPoll: refresh, }); - const latestReady = useMemo( - () => state.reports.find((report) => report.status === "ready") ?? null, - [state.reports], - ); - const removeReport = useCallback((id: string) => { deletedIds.current.add(id); invalidateReportsPage(); @@ -161,15 +156,6 @@ export function PersonalReportCenter() { actions={ void load()} />} >
-
-
-

有填报到分钟的出生时间即可生成;未校正会标明为方向性参考。只有日期和时段时,完整本命报告需要具体分钟,生时校正可选。与某一次对话无关。创建后会在后台继续处理,你无需停留等待。

-

- 共 {state.reports.length} 份 · 已完成 {state.reports.filter((report) => report.status === "ready").length} 份 · {hasGenerating ? "有报告生成中" : latestReady ? "最新报告可查看" : "尚待生成"} -

-
-
-

报告记录

diff --git a/frontend/tests/personal-report-entry.test.ts b/frontend/tests/personal-report-entry.test.ts index 8c6532fd..6abfb79b 100644 --- a/frontend/tests/personal-report-entry.test.ts +++ b/frontend/tests/personal-report-entry.test.ts @@ -185,10 +185,14 @@ test("no birth data or chat text is sent from the entry component", () => { test("global report copy uses the canonical profile, allows an unrectified minute, and makes background work explicit", () => { assert.match(componentSource, /authenticated user's canonical profile/); - assert.match(reportCenterSource, /有填报到分钟的出生时间即可生成/); - assert.match(reportCenterSource, /完整本命报告需要具体分钟/); - assert.match(reportCenterSource, /与某一次对话无关/); - assert.match(reportCenterSource, /后台继续处理/); + // 原值: 报告中心顶部说明段必须含「有填报到分钟的出生时间即可生成」「完整本命报告需要具体分钟」「与某一次对话无关」「后台继续处理」四句(四条 match) + // 新值: 四句都不再出现(doesNotMatch),顶部说明区与统计行整块删除;生成按钮的 title 仍说明分钟要求(下方断言不动) + // 原因: TASK-self-edit-avatar-menu-20260928 S3——产品要求「我的报告」去掉顶部提示,页面从「报告记录」列表开始 + assert.doesNotMatch(reportCenterSource, /有填报到分钟的出生时间即可生成/); + assert.doesNotMatch(reportCenterSource, /完整本命报告需要具体分钟/); + assert.doesNotMatch(reportCenterSource, /与某一次对话无关/); + assert.doesNotMatch(reportCenterSource, /后台继续处理/); + assert.doesNotMatch(reportCenterSource, /report-center-hero|report-center-meta|报告概览/); assert.match(componentSource, /根据已保存的具体出生分钟生成完整报告/); assert.doesNotMatch(reportCenterSource, /基于你已确认的出生资料生成/); assert.doesNotMatch(componentSource, /根据已确认的个人出生资料生成完整报告/);