fix(ui): restyle personal report pages to the product reading room
The report centre and reader were a second newspaper identity. They now reuse canvas, hairline, and serif display tokens so the document feels like the rest of Jyotisha. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,6 +20,8 @@ import { useRouter } from "next/navigation";
|
||||
import { FileText } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export const DEFAULT_REPORT_THEMES = ["career", "marriage", "wealth", "timing", "health"] as const;
|
||||
|
||||
export interface PersonalReportCreateRequest {
|
||||
@@ -190,15 +192,15 @@ export function GeneratePersonalReportButton({
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
className="inline-flex min-h-11 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-lg border border-border bg-canvas px-3 py-1.5 text-sm text-ink transition-colors hover:bg-canvas-muted disabled:pointer-events-none disabled:opacity-50"
|
||||
className="shrink-0 whitespace-nowrap"
|
||||
onClick={() => void handleGenerate()}
|
||||
disabled={submitting}
|
||||
title="根据已保存的具体出生分钟生成完整报告;未校正会标明方向性参考"
|
||||
>
|
||||
<FileText aria-hidden="true" className="size-4" />
|
||||
<FileText aria-hidden="true" />
|
||||
{submitting ? "正在创建…" : "生成完整报告"}
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ export function PersonalReportCenter() {
|
||||
return (
|
||||
<article className="report-center-card" key={report.id}>
|
||||
<div className="report-center-card-body">
|
||||
<div className={`report-center-status is-${report.status}`}>
|
||||
<div className={`report-center-status is-${report.status}`} role={report.status === "generating" ? "status" : undefined}>
|
||||
<StatusIcon status={report.status} />{copy.label}
|
||||
</div>
|
||||
<h3>{report.reportType === "personal_thematic" ? "个人主题报告" : "个人完整报告"}</h3>
|
||||
@@ -229,7 +229,7 @@ export function PersonalReportCenter() {
|
||||
<small>{formatDate(report.createdAt)} · {report.depth} · {report.themes.join(" / ") || "综合主题"}</small>
|
||||
</div>
|
||||
{report.status === "ready" ? (
|
||||
<div className="flex flex-wrap items-center justify-end gap-2 max-[720px]:justify-start">
|
||||
<div className="report-center-card-actions">
|
||||
<Button render={<Link href={`/reports/${encodeURIComponent(report.id)}`} />} nativeButton={false} variant="outline">
|
||||
查看报告
|
||||
</Button>
|
||||
@@ -243,7 +243,7 @@ export function PersonalReportCenter() {
|
||||
专业参考版(导出)
|
||||
</Button>
|
||||
{exportError?.reportId === report.id ? (
|
||||
<span className="basis-full text-right text-sm text-destructive max-[720px]:text-left" role="alert">
|
||||
<span className="report-center-export-error" role="alert">
|
||||
{exportError.message}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@@ -197,6 +197,7 @@ function ReportCover({ document, d1 }: { document: ReportDocument; d1: ChartV1 |
|
||||
<header className="personal-report-cover personal-report-section">
|
||||
<div className="personal-report-cover-grid">
|
||||
<div>
|
||||
<p className="personal-report-kicker">个人报告</p>
|
||||
<h1>{document.subject.displayName}</h1>
|
||||
<p className="personal-report-deck">{document.executiveSummary.headline}</p>
|
||||
</div>
|
||||
|
||||
@@ -229,17 +229,15 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "loading" || state.phase === "generating") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<main className="personal-report-state">
|
||||
<InlineSpinner className="text-primary" size={32} />
|
||||
<p className="text-ink" role="status">
|
||||
<p role="status">
|
||||
{generating ? (progressLabel ?? "报告正在生成中,请稍候…") : "正在加载报告…"}
|
||||
</p>
|
||||
{generating && (
|
||||
<>
|
||||
<p className="text-sm text-ink-tertiary">已等待 {formatWaitedDuration(waitedMs)}</p>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
生成完成后页面会自动显示;你也可以返回报告中心,后台会继续处理。
|
||||
</p>
|
||||
<p>已等待 {formatWaitedDuration(waitedMs)}</p>
|
||||
<p>生成完成后页面会自动显示;你也可以返回报告中心,后台会继续处理。</p>
|
||||
<Button render={<Link href="/reports" />} nativeButton={false} variant="ghost">返回报告中心</Button>
|
||||
</>
|
||||
)}
|
||||
@@ -249,13 +247,11 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "timed-out") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<main className="personal-report-state">
|
||||
<Clock3 aria-hidden="true" className="size-8 text-ink-secondary" />
|
||||
<h1 className="text-xl font-semibold text-ink">生成时间超出预期</h1>
|
||||
<p className="text-sm text-ink-tertiary">已等待 {formatWaitedDuration(waitedMs)},页面已暂停自动刷新。</p>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
报告仍在后台生成,不会因为你离开而中断。你可以继续等待,也可以稍后回到报告中心查看结果。
|
||||
</p>
|
||||
<h1>生成时间超出预期</h1>
|
||||
<p>已等待 {formatWaitedDuration(waitedMs)},页面已暂停自动刷新。</p>
|
||||
<p>报告仍在后台生成,不会因为你离开而中断。你可以继续等待,也可以稍后回到报告中心查看结果。</p>
|
||||
<div className="flex flex-wrap items-center justify-center gap-3">
|
||||
<Button type="button" variant="default" onClick={() => keepWaiting()}>
|
||||
继续等待
|
||||
@@ -270,11 +266,9 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "unauthorized") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<h1 className="text-xl font-semibold text-ink">请先登录</h1>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
个人报告仅对登录用户开放。请登录后重试。
|
||||
</p>
|
||||
<main className="personal-report-state">
|
||||
<h1>请先登录</h1>
|
||||
<p>个人报告仅对登录用户开放。请登录后重试。</p>
|
||||
<Button render={<Link href="/login" />} nativeButton={false} variant="default">
|
||||
去登录
|
||||
</Button>
|
||||
@@ -284,11 +278,9 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "not-found") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<h1 className="text-xl font-semibold text-ink">报告不存在</h1>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
该报告不存在、已删除,或不属于当前账号。
|
||||
</p>
|
||||
<main className="personal-report-state">
|
||||
<h1>报告不存在</h1>
|
||||
<p>该报告不存在、已删除,或不属于当前账号。</p>
|
||||
<Button render={<Link href="/reports" />} nativeButton={false} variant="outline">
|
||||
返回报告中心
|
||||
</Button>
|
||||
@@ -298,14 +290,12 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "failed") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<main className="personal-report-state">
|
||||
<TriangleAlert aria-hidden="true" className="size-8 text-warning" />
|
||||
<h1 className="text-xl font-semibold text-ink">报告生成失败</h1>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
{state.failureSummary ?? "生成过程中出现问题,未产出可用报告。请稍后重试。"}
|
||||
</p>
|
||||
<h1>报告生成失败</h1>
|
||||
<p>{state.failureSummary ?? "生成过程中出现问题,未产出可用报告。请稍后重试。"}</p>
|
||||
{state.failureCode && (
|
||||
<p className="font-mono text-xs text-ink-tertiary">错误码:{state.failureCode}</p>
|
||||
<p>错误码:{state.failureCode}</p>
|
||||
)}
|
||||
<Button render={<Link href="/reports" />} nativeButton={false} variant="outline">
|
||||
返回报告中心重新生成
|
||||
@@ -316,10 +306,10 @@ export function PersonalReportPage({ reportId }: { reportId: string }) {
|
||||
|
||||
if (state.phase === "invalid" || state.phase === "network-error") {
|
||||
return (
|
||||
<main className="flex min-h-svh flex-col items-center justify-center gap-4 px-4 text-center">
|
||||
<main className="personal-report-state">
|
||||
<TriangleAlert aria-hidden="true" className="size-8 text-danger" />
|
||||
<h1 className="text-xl font-semibold text-ink">报告暂时无法显示</h1>
|
||||
<p className="max-w-md text-sm text-ink-secondary">
|
||||
<h1>报告暂时无法显示</h1>
|
||||
<p>
|
||||
{state.phase === "invalid"
|
||||
? `报告数据未通过校验(${state.message}),已停止渲染。`
|
||||
: "网络连接失败,请检查网络后重试。"}
|
||||
|
||||
@@ -91,7 +91,6 @@ function PlanetList({ lines }: { lines: string[] }) {
|
||||
x={6 + column * 49}
|
||||
y={45 + row * rowStep}
|
||||
fontSize={fontSize}
|
||||
fill="#1d1d1f"
|
||||
>
|
||||
{truncateSvgLabel(line, maxCharacters)}
|
||||
</text>
|
||||
@@ -141,15 +140,13 @@ export function VedicChartSvg({ chart, ariaLabel }: VedicChartSvgProps) {
|
||||
y={cell.y}
|
||||
width={CELL}
|
||||
height={CELL}
|
||||
fill="none"
|
||||
stroke="#32322f"
|
||||
strokeWidth={1.25}
|
||||
className="personal-report-chart-cell"
|
||||
/>
|
||||
<text x={cell.x + 5} y={cell.y + 15} fontSize={10} fill="#6a6963">
|
||||
<text x={cell.x + 5} y={cell.y + 15} fontSize={10} className="personal-report-chart-muted">
|
||||
{houseNumber}
|
||||
</text>
|
||||
{house && (
|
||||
<text x={cell.x + 5} y={cell.y + 30} fontSize={12} fontWeight={600} fill="#32322f">
|
||||
<text x={cell.x + 5} y={cell.y + 30} fontSize={12} fontWeight={600}>
|
||||
{house.sign}
|
||||
</text>
|
||||
)}
|
||||
@@ -164,18 +161,14 @@ export function VedicChartSvg({ chart, ariaLabel }: VedicChartSvgProps) {
|
||||
y={CELL}
|
||||
width={CELL * 2}
|
||||
height={CELL * 2}
|
||||
fill="#f3f2ee"
|
||||
stroke="#32322f"
|
||||
strokeWidth={1.25}
|
||||
className="personal-report-chart-core"
|
||||
/>
|
||||
<path
|
||||
d={`M ${CELL * 2} ${CELL} L ${CELL * 3} ${CELL * 2} L ${CELL * 2} ${CELL * 3} L ${CELL} ${CELL * 2} Z`}
|
||||
fill="none"
|
||||
stroke="#32322f"
|
||||
strokeWidth={1.25}
|
||||
className="personal-report-chart-diamond"
|
||||
/>
|
||||
{hasRetrogradeMarker && (
|
||||
<text x={CHART_VIEWBOX_WIDTH - 6} y={CHART_VIEWBOX_HEIGHT - 4} textAnchor="end" fontSize={9} fill="#6a6963">
|
||||
<text x={CHART_VIEWBOX_WIDTH - 6} y={CHART_VIEWBOX_HEIGHT - 4} textAnchor="end" fontSize={9} className="personal-report-chart-muted">
|
||||
“逆”=逆行
|
||||
</text>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user