Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
18 KiB
18 KiB
TASK · 设置弹窗重排(固定尺寸、列表→详情)与「账户与点数」入弹窗、删除 /membership 整页(2026-09-06)
- 基线:
origin/staging@985c3258 - 分支:
codex/settings-dialog-and-billing-pane-20260906 - 执行方:coding agent;验收:Claude
- 涉及文件:
frontend/src/components/account-dialog-overlay.tsx、frontend/src/components/chart-library-panel.tsx、新建frontend/src/components/profile-panel.tsx、新建frontend/src/components/billing-panel.tsx(+frontend/src/hooks/use-billing-panel.ts或frontend/src/lib/billing-order-polling.ts)、新建frontend/src/lib/settings-url.ts、frontend/src/lib/home-types.ts、frontend/src/lib/membership.ts、frontend/src/app/page.tsx(只改传参与调用,必须净减行:renderProfile的 JSX 整段搬出)、frontend/src/components/app-sidebar.tsx、frontend/src/components/onboarding-redeem-paywall.tsx、frontend/src/components/rectification-agentic-chat.tsx、frontend/src/hooks/use-consultation-run.ts、frontend/src/app/globals.css、frontend/next.config.ts、删除frontend/src/app/membership/page.tsx与frontend/src/app/membership/orders/page.tsx、tests/test_api_server_security.py(路由集合)、frontend/DESIGN.md、frontend/docs/VOICE.md - BUG 编号起点:BUG-554(BUG-553 由
TASK-session-list-title-and-order-20260906.md预留;开工时复核最大号) - 串行:同日顺序 composer(551/552)→ 会话列表(553)→ 本单。本单大改
page.tsx、app-sidebar.tsx、use-consultation-run.ts、rectification-agentic-chat.tsx,必须在前两单合入 staging 后再开分支。 - 删除页面属于路由集合变更:本单必须在同一变更里跑
.venv/bin/python -m pytest tests/test_api_server_security.py -k capability_audit(BUG-143 的防复发要求),不得只跑前端矩阵。
1. 事故实证(staging,2026-09-06,桌面 Chrome,产品截图三张)
| # | 用户看到 | 期望(对标 Claude 设置弹窗) |
|---|---|---|
| 1 | 「星盘资料」格:本人资料条与「编辑本人资料」按钮之后,空态一句「还没有其他星盘,先添加一份资料」紧接一整张「添加其他人的星盘」表单(关系下拉、称呼、出生日期、「你对出生时间了解多少」单选组……)铺在同一个滚动区里,弹窗撑到接近整屏 | 列表是列表,表单是点了「添加」之后才出现的二级视图 |
| 2 | 三个分区弹窗尺寸各不相同:「通用设置」是一个只装得下主题三选的小盒子,「个人资料」「星盘资料」是宽而高的大盒子,切换分区整个弹窗跳变 | 一个固定尺寸的设置窗,左侧导航不动,右侧内容区自己滚 |
| 3 | 「个人资料」格:96px 头像预览 + 8 个配色块 + 「换一个形象」占了上半屏,账户信息两行,底部又一个「管理星盘资料」按钮(与左侧导航重复) | 头像一行搞定;重复入口删除 |
| 4 | 左侧导航第四项「账户与点数」点下去离开首页跳 /membership 整页,弹窗关闭,返回后要重新加载 |
它就是第四个分区 |
| 5 | 「星盘资料」里每一条其他人资料一行塞下 关系下拉 + 用于合盘 + 编辑 + 设为默认 + 删除;合盘结果卡和合盘历史平铺在列表下面 | 列表条只显示人;操作在这个人的详情里 |
2. 根因(行号以基线为准)
chart-library-panel.tsxL170–282:一个<div className="chart-library-panel">顺序渲染本人组 → 本人编辑表单(editingSelfChart)→ 其他人组 → 合盘卡 → 合盘历史 → 无条件渲染的「添加 / 编辑其他人」表单(L263)。没有视图状态,只有堆叠。globals.cssL1637–1639:.account-modal { width: min(100%, 560px); max-height: min(84dvh, 760px) },.chart-library-modal { width: min(100%, 760px) },profile-modal另有 sticky 头;高度由内容决定,所以主题格是小盒、资料格是大盒。home-types.tsL191accountDialogClasses给每个分区一个不同的 modal class,是尺寸分裂的源头。page.tsxL1620–1670renderProfile把头像编辑区与账户信息 JSX 直接写在page.tsx里(约 50 行),末尾 L1668<button …>管理星盘资料</button>只是openAccountDialog("chart-library")——与导航项重复。account-dialog-overlay.tsxL88 第四个导航项没有dialog值,直接model.openRedeem→page.tsxL1618router.push(membershipHref("account-menu"))。AccountDialog类型(home-types.tsL135)只有profile | chart-library | general | logout,没有 billing 分区。/membership是独立页面(membership/page.tsx583 行 +orders/page.tsx127 行):套餐两页签、兑换码子弹窗、订单入口;支付走/api/payment/epay/create→window.open(payUrl)→ 每 3 秒轮询/api/payment/epay/status(可见性感知),这套完全能装进弹窗。入口共 7 处:page.tsxL1618/L1782/L1810、onboarding-redeem-paywall.tsxL129(<Link>)、rectification-agentic-chat.tsxL825(window.location.assign,硬跳转)、use-consultation-run.tsL527/L722(router.push)。
3. 决策记录(产品已授权,2026-09-06)
- 一个固定尺寸的设置弹窗:桌面
width: min(100vw - 32px, 880px); height: min(84dvh, 640px);左栏导航 176px 固定不滚,右栏内容独立overflow-y: auto;标题栏固定在顶部。四个分区共享同一尺寸,切换分区不改变外框。accountDialogClasses里profile-modal / chart-library-modal / general-modal三个 class 合并为一个settings-modal(logout-modal保留,退出确认仍是 400px 小弹窗)。≤767px 保持现有全屏 + 顶部四格页签的布局,只把内容区改成独立滚动。 - 分区顺序与内容:个人资料 → 星盘资料 → 账户与点数 → 通用设置。退出登录仍在头像菜单,不进导航。
- 个人资料格:一行头像编辑(当前头像 48px + 8 个配色块 + 「换一个形象」文字按钮),账户信息(昵称、登录邮箱)两行。删除「管理星盘资料」按钮。整段 JSX 搬进
profile-panel.tsx,page.tsx只传 props。 - 星盘资料格改为列表 → 详情:
- 列表视图:「我的星盘」一条(名字、出生摘要、「当前默认」标签);「其他人」若干条(名字、关系标签、出生摘要);底部一个「添加其他人」按钮。列表条整条可点,右侧一个 chevron,不再一行五个按钮。
- 详情视图(同一内容区内切换,顶部一个「← 星盘资料」返回):本人详情 = 编辑表单;其他人详情 = 编辑表单 + 操作行(设为默认 / 删除)+ 「合盘」小节(关系类型 + 「用于合盘」)+ 该人的合盘记录(现有
synastryHistory按partnerName归属,新记录保存时顺手带上partnerChartId,执行方可以只加字段不做迁移;无记录不渲染小节)。 - 添加视图:点「添加其他人」进入,与其他人详情共用同一表单组件,保存后回到列表。
- 全局的「合盘结果摘要」卡与「合盘历史」列表从列表视图删除(决策 4 的详情视图接管);合盘触发后弹窗关闭、问题进入输入框的既有行为不变。
- 视图状态(
list | self | other:<id> | add)放在chart-library-panel.tsx内部或frontend/src/lib/chart-library-view.ts纯函数里,不进page.tsx。
- 「账户与点数」成为第四分区(
AccountDialog增加"billing"):顶部一行摘要(余额 N 点 · 会员状态 + 到期);下面四个页签:会员套餐 / 点数包 / 兑换码 / 订单记录。套餐与点数包卡片、购买按钮、支付轮询、失败重试语义原样搬(membership-page.test.ts锁住的所有行为都要在新组件里成立);兑换码不再是子弹窗,是页签内的行内表单;订单记录页签内直接列/api/payment/orders。plan高亮参数保留(membershipHref(source, { plan })传来的推荐套餐卡片高亮并滚到可见)。 - 删除
/membership与/membership/orders两个页面(产品明确要求,不是重定向壳)。next.config.ts加redirects():/membership→/?settings=billing&source=legacy,/membership/orders→/?settings=billing&tab=orders(permanent: false)。membershipHref()改为生成/?settings=billing&source=…[&plan=…],只给深链接与重定向用。 - 入口统一走回调,不走路由:
page.tsx三处、app-sidebar.tsx头像菜单、onboarding-redeem-paywall.tsx(<Link>改为按钮回调,付费墙关闭后打开设置弹窗 billing 格)、rectification-agentic-chat.tsx的 402、use-consultation-run.ts的两处余额不足,全部改为调用openAccountDialog("billing", { source, plan })(hooks 与组件通过参数接收该回调,遵守参数式 hook 内 0 个 React hook 的既定模式)。?settings=billing参数只在首页启动与 popstate 时由lib/settings-url.ts::parseSettingsQuery(search)读取一次,打开弹窗后history.replaceState抹掉参数——与既有chat-session-url.ts的做法一致,/保持 Static。 - 等待态:弹窗打开时账户余额已在内存里(
account),摘要行直接渲染;套餐卡片来自/api/payment/packages,到达前该区域留空,不得出现 spinner / 骨架 / 「正在加载」(AGENTS §6)。购买中按钮文案「跳转收银台…」与轮询中的「等待支付结果」作为按钮/行内状态文字保留(这是流程中的状态,不是加载动画)。 billing-panel.tsx通过next/dynamic(ssr: false)按需加载,保证首屏 gzip 不因搬入而增长。
4. 硬红线
page.tsx不得增行,本单必须净减(renderProfileJSX 搬走);进度记录写开工 / 收尾行数。- 不改支付、兑换、订单三组 API 与其 zod 契约;不改管理端;不动数据库迁移。
- 硬跳转清单(
chat-navigation-a11y-contract.test.ts锁定)只能缩小,不得新增window.location.assign;/login认证重定向保持硬跳转。 - 收银台仍用
window.open新标签打开(BUG-251 与导航合同测试都要求原生行为)。 - 焦点陷阱、Escape 关闭、关闭后焦点回到触发按钮、
aria-modal等既有可达性行为不变(account-dialog-overlay.test.ts)。 - 删除页面后
git grep -n "/membership" frontend/src只允许命中next.config.ts的重定向与membership.ts里生成?settings=billing的注释;membership-page.test.ts、membership-navigation-contract.test.ts的 26 条测试迁移到新组件文件而不是删除,总数不降,每条改动在三栏表里说明。 tests/test_api_server_security.py的精确路由集合去掉membership与membership/orders,同一提交里跑通。- 任务书 / 进度 / Bug 历史 / 测试 fixture 不得出现产品截图里的昵称与邮箱;示例只用虚构数据。
- 改 UI 的同一提交更新
frontend/DESIGN.md:「Profile dialog」改写为「Settings dialog」(固定尺寸、四分区、列表→详情),删除「Redeem dialog」「Membership」两节,新增「Billing pane」;「Account popover」的 redeem 行改指向 billing 分区。
5. 任务分解
5.1 弹窗壳:固定尺寸 + 四分区(BUG-554 的一半)
home-types.ts:AccountDialog = "profile" | "chart-library" | "billing" | "general" | "logout";accountDialogTitles加「账户与点数」;accountDialogClasses收敛为settings-modal(四个分区)+logout-modal。account-dialog-overlay.tsx:导航数组加 billing 项(图标沿用WalletCards),删openRedeem;AccountOverlayModel加renderBilling;openAccountDialog支持{ source, plan, tab }附加参数(放进 model 传给 billing 面板)。globals.css:.settings-modal固定尺寸、.account-settings-shell改为grid-template-rows: auto 1fr+ 内容区min-height: 0; overflow-y: auto;删除.chart-library-modal/.profile-modal专属 sticky 头规则;移动端规则只改内容区滚动。- 验收:
tests/account-dialog-overlay.test.ts加——四个分区导航项齐全且billing有aria-current;不存在openRedeem;globals.css中.settings-modal同时声明width与height,且chart-library-modal/profile-modal不再出现。
5.2 个人资料格搬出 page.tsx
- 新建
profile-panel.tsx(props:account、profile、avatarSaving、avatarNotice、accountError、persistAvatar);头像一行布局;删除「管理星盘资料」按钮。 - 验收:
page.tsx行数净减 ≥ 40;tests/account-dialog-overlay.test.ts或新建tests/profile-panel.test.ts:不含「管理星盘资料」;头像配色 radiogroup 与「换一个形象」仍在。
5.3 星盘资料格:列表 → 详情(BUG-554 的另一半)
- 按决策 4 重做
chart-library-panel.tsx;表单组件抽成chart-profile-form.tsx(本人 / 其他人共用,ProfileFields+AyanamsaPreferenceField不动)。 - 验收:
tests/chart-library-other-profile.test.ts、tests/chart-library-session.test.ts按三栏表改;新增用例——初始视图为列表且源码中不存在无条件渲染的<form;「添加其他人」进入 add 视图、保存后回到列表;其他人详情包含 设为默认 / 删除 / 用于合盘;本人详情无删除;合盘记录按人归属、无记录不渲染。
5.4 「账户与点数」分区(CHANGELOG 项)
- 新建
billing-panel.tsx+use-billing-panel.ts(账户 / 套餐并发拉取、下单、window.open、可见性感知轮询与清理、失败态、兑换、订单列表;逻辑从membership/page.tsx搬,删除goBack、membership-loading、子弹窗焦点陷阱);page.tsx的renderBilling用next/dynamic挂载。 - 删除两个页面文件及其专属 CSS(
membership-page / membership-shell / membership-header* / membership-back / membership-hero*等只服务整页的规则),卡片、页签、按钮样式在.billing-panel下复用。 next.config.ts加两条重定向;membership.ts::membershipHref改生成?settings=billing;新建lib/settings-url.ts(parseSettingsQuery/stripSettingsQuery纯函数)。- 七处入口改回调(决策 7)。
- 验收:
tests/billing-panel.test.ts承接membership-page.test.ts与membership-navigation-contract.test.ts的全部 26 条(逐条对照:下单带productId并安全打开收银台;终态失败停轮询并可用同一商品重试;failed优先于paid、非终态继续轮询;支付成功只刷新余额并notifyBalanceChanged;兑换码去空白不改大小写;兑换成功报点数与最新余额;plan参数高亮对应卡;隐藏页签暂停轮询、回来刷新;清理释放计时器与监听;账户与套餐并发请求;一个请求失败不隐藏另一区;401 仍硬跳/login);tests/settings-url.test.ts新建(?settings=billing&source=x&plan=monthly&tab=orders解析、无参数返回 null、strip 后其他参数保留);chat-navigation-a11y-contract.test.ts三栏表更新:软导航清单中 4 条membershipHref改为回调断言,硬跳转清单减少 1 条(rectification-agentic-chat的 402);tests/test_api_server_security.py -k capability_audit通过;next build输出中不再有/membership。
5.5 记录
docs/BUG_HISTORY.md:BUG-554(设置弹窗尺寸随分区跳变 + 星盘资料格无条件铺开整张表单;关联 BUG-434 弹窗样式记录、BUG-251 导航记录)。CHANGELOG.md:设置弹窗固定尺寸与四分区、星盘资料列表→详情、账户与点数入弹窗、/membership页面删除并重定向。frontend/DESIGN.md(§4 硬红线最后一条);frontend/docs/VOICE.md:分区名「账户与点数」、页签名「会员套餐 / 点数包 / 兑换码 / 订单记录」、列表条与详情返回的文案。docs/tasks/PROGRESS-settings-dialog-and-billing-pane-20260906.md;docs/testing/settings-dialog-and-billing-pane-20260906.md(真实环境:四个分区切换外框不动;星盘资料默认只有列表;添加→保存→回列表;点余额、头像菜单、余额不足 402、付费墙四个入口都在当前页打开弹窗且对话状态不丢;下单打开新标签、回来后余额刷新;旧链接/membership落到首页并自动打开分区;手机端全屏四页签内容可滚)。
6. 让步顺序
5.1 → 5.2 → 5.4 → 5.3 → 5.5。若 5.3 的详情视图在本轮做不完,允许先只做「添加表单默认收起、点按钮展开」并在进度记录写明,但 5.1、5.2、5.4 与页面删除不可拆、不可省。
7. 开工前置命令
git fetch origin --prune
git worktree add -b codex/settings-dialog-and-billing-pane-20260906 .worktrees/settings-dialog-and-billing-pane-20260906 origin/staging
cd .worktrees/settings-dialog-and-billing-pane-20260906
ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules
wc -l frontend/src/app/page.tsx
cd frontend && npx tsx --test tests/account-dialog-overlay.test.ts tests/chart-library-*.test.ts tests/membership-*.test.ts tests/chat-navigation-a11y-contract.test.ts tests/epay-*.test.ts tests/redeem-orders-contract.test.ts 2>&1 | grep -E "^# (tests|pass|fail)|^not ok"
收尾:同一条命令 fail=0 且总数不低于开工;tsc --noEmit、npm run lint(0 error)、npm test、npm run build(/ Static、gzip ±2%、无 /membership 路由);cd .. && .venv/bin/python -m pytest tests/test_api_server_security.py -k capability_audit。