feat(ui): action results and errors are top-centre toasts, never inline bars; login card centred (BUG-1121, BUG-1122)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-30 13:46:01 +08:00
co-authored by Claude Opus 5.5
parent d327ca9c95
commit 3e0c615fd8
61 changed files with 523 additions and 372 deletions
+13 -2
View File
@@ -567,9 +567,20 @@ one skeleton, none of them carrying the sidebar.
The ephemeris, the report list and the people archive (2026-09-30) wait with one motion: the home loading screen's orbit ring at 40px (`OrbitWaiting`), centred in the page. No sentence is shown; the old 「……还没拿到。」 / 「人物列表还在读」 copy is kept only as the screen-reader status. The person picker popover shows the same orbit while its list loads. The chart page is the exception (BUG-1016): since 2026-09-30 the empty chart itself is the wait — faint still lines while the houses light up one after another in house order — with no orbit on top.
### 提示与报错:toast(2026-09-30)
Action results and errors never take page layout. They are toasts from `src/lib/notify.ts` (`notifySuccess` / `notifyInfo` / `notifyError`; chat notices keep `showChatNotice`, same toaster and durations).
- **Where:** top centre on every screen (`components/ui/sonner.tsx`, `position="top-center"`), 16px under the safe area; phones get 16px side gutters and full width. The composer lives at the bottom on phones, so the top is the only place that never covers it.
- **Look:** a compact `--color-canvas` card, 1px `--color-border`, `--radius-lg`, `--shadow-elevated`, body-sm text, a coloured icon (success `--color-success`, error `--color-danger`, info `--color-ink-secondary`). No left bar, no tinted background.
- **How long:** success and info 3s; errors 6s with a close button on the right; the staging fixed login code 10s. Messages from one surface share an id, so a new one replaces the last instead of stacking (login uses `auth-notice` / `auth-error`; typing dismisses the stale error).
- **Above modal dialogs:** a toast raised while a modal `<dialog>` is open (反馈与投诉, 导出, 协议更新, 那一刻的天空) moves the toaster into the top layer as a manual popover, re-shown on every toast, so it is never under the dialog's backdrop.
- **Errors held in flow state** (the birth-time guided flow) are said through `<ErrorToast message={error} />`: once per new message, id = message.
- **What stays in the page:** states that are the page's content, not a report about an action — a pane that failed to load with its own 重试 (chart, ephemeris, report list, report detail, the home bootstrap error, billing packages / payment status); empty states; the failed reply inside a conversation (`.error-message`) and the stopped-reply line; rectification progress and gap notes; the feedback dialog's done panel; a field's own validation inside the evidence draft card. What remains of `.form-error` is plain `--color-danger` caption text, no bar and no background; `.form-success` is gone.
### Login consent and the legal pages (2026-09-30)
- **Login:** under the form, an unticked checkbox 「我已阅读并同意《用户协议》和《隐私政策》」 (`.auth-consent`, caption size, links underlined, open in a new tab). Sending a code or a password sign-in with the box unticked does nothing but show one red caption line under it; ticking clears it. The operators' admin login (password-only, `/admin`) has no box. A centred caption footer under the login card links 用户协议 · 隐私政策 and, once configured, the ICP number to beian.miit.gov.cn.
- **Login:** under the form, an unticked checkbox 「我已阅读并同意《用户协议》和《隐私政策》」 (`.auth-consent`, caption size, links underlined, open in a new tab). Sending a code or a password sign-in with the box unticked does nothing but mark the box (a `--color-danger` outline, `.auth-consent.is-attention`) and raise the error toast 「请先阅读并勾选同意,再继续。」; ticking clears the mark. The operators' admin login (password-only, `/admin`) has no box. The login page is a three-row grid (`1fr auto 1fr`): the card sits in the middle row, exactly centred, and the caption footer 用户协议 · 隐私政策 (plus the ICP number to beian.miit.gov.cn once configured) takes the bottom row, so it never pushes the card up (BUG-1122). Below 768px the rows are `1fr auto` and the footer sits under the full-height card.
- **Account menu:** a caption row at the very bottom, 「用户协议 · 隐私政策」, below 退出登录.
- **/terms and /privacy:** public, static, one 720px column on `--color-canvas-soft`; a top row with 「返回 Jyotisha」 and a link to the other document; while the text is a draft, a warning-tinted note 「草稿 · 版本 …,待律师审定」 above the title; h1 in the display face, h2 per section, bullet lists for enumerations.
- **Re-consent:** when a final version replaces the one a signed-in user accepted, one modal 「协议已更新」 with the same checkbox and 「同意并继续」; it cannot be dismissed without agreeing. Off while the documents are a draft.
@@ -593,7 +604,7 @@ The ephemeris, the report list and the people archive (2026-09-30) wait with one
- **Metadata and deletion:** full Chinese date · localized depth · localized themes; unknown IDs remain readable verbatim. Every row has a 44px top-right `⋯` menu containing only 「删除」. It opens the row-local 「确定删除这份报告?」 with 删除 / 取消, never `window.confirm`. Failure stays in that row; success removes it and invalidates list cache. In-flight GET responses cannot resurrect a deleted row.
- **Width:** 900px centered, matching long-form chat reading. Rows stack at the global 767px cut.
- **Actions:** “生成报告” in the generate card is the one filled action. A ready row keeps only “查看报告”; export is selected inside the reader so the list does not duplicate that action. Generating copy is one-step (“正在生成报告,大约 10–30 秒”); there is no chapter-count progress. A failed row states the reason in place and offers no second generate entry — the card above already has one. Card summary is a deterministic excerpt of the Markdown 「摘要」 section, stored on the cover document at generation time.
- **States:** loading, empty, populated, generating, ready, failed, unauthorized, list error. First-screen loading uses `SecondaryPageShell` waiting copy 「报告列表还没拿到。」 — never a spinner, skeleton, or 「正在加载」. Export is local to the ready reader's drawer and never starts a second writing flow or fetch. Download failures remain in the open drawer; only success closes it and emits a toast. A missing Markdown appendix is an old report: the detail page asks the reader to regenerate.
- **States:** loading, empty, populated, generating, ready, failed, unauthorized, list error. First-screen loading uses `SecondaryPageShell` waiting copy 「报告列表还没拿到。」 — never a spinner, skeleton, or 「正在加载」. Export is local to the ready reader's drawer and never starts a second writing flow or fetch. Download failures raise an error toast and leave the drawer open; only success closes it and emits the 「已导出」 toast. A missing Markdown appendix is an old report: the detail page asks the reader to regenerate.
- **Accessibility:** generate, refresh, and row actions are 44px. The generating row's chip is a live region; a failed export is an alert.
### Personal report reader