Files
Jyotisha/frontend/src/components/starter-home.tsx
T
Jesse_ChenandCursor c53decdb85 fix(consult): complete natal chart tool on homepage topic cards (BUG-630)
Thinking models were spending the first natal step on skill_read or a Level 2 draft, so home chips such as 家庭 never satisfied the calculation contract.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 22:46:46 +08:00

166 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { ArrowUpRight } from "lucide-react";
import type { ConsultationEntrypoint } from "@/lib/consultation-entrypoint";
import type { DailyStarlanguageState, Theme } from "@/lib/home-types";
import type { RectificationEntrySummary } from "@/lib/rectification-entry";
export type StarterHomeTheme = {
readonly id: string;
readonly label: string;
readonly prompt: string;
};
export type StarterHomeSuggestion = {
readonly theme: Theme;
readonly text: string;
};
export type StarterHomeProps = {
readonly starterGreeting: { readonly salutation: string; readonly question: string };
readonly dailyStarlanguage: DailyStarlanguageState;
readonly natalMinuteAvailable: boolean;
readonly dailyStarlanguageQuestion: string;
readonly dailyStarlanguageTrend: string;
readonly dailyStarlanguageAction: string;
readonly productEntrypointsDisabled: boolean;
readonly startDailyStarlanguageConsultation: () => void;
readonly rectificationCardLabel: string;
readonly rectificationCardAction: string;
readonly rectificationLoading: boolean;
readonly rectificationMutationPending: boolean;
readonly openRectificationFromHomepage: () => void;
readonly rectificationEntrySummary: RectificationEntrySummary | null;
readonly rectificationError: string;
readonly rectificationSurfaceOpen: boolean;
readonly rectificationErrorMessage: string;
readonly starterThemes: readonly StarterHomeTheme[];
readonly starterSuggestions: readonly StarterHomeSuggestion[];
readonly startSuggestedConsultation: (
text: string,
theme: Theme,
entrypoint?: ConsultationEntrypoint | null,
) => void;
readonly onboardingError: string;
};
export function StarterHome({
starterGreeting,
natalMinuteAvailable,
dailyStarlanguage,
dailyStarlanguageQuestion,
dailyStarlanguageTrend,
dailyStarlanguageAction,
productEntrypointsDisabled,
startDailyStarlanguageConsultation,
rectificationCardLabel,
rectificationCardAction,
rectificationLoading,
rectificationMutationPending,
openRectificationFromHomepage,
rectificationEntrySummary,
rectificationError,
rectificationSurfaceOpen,
rectificationErrorMessage,
starterThemes,
starterSuggestions,
startSuggestedConsultation,
onboardingError,
}: StarterHomeProps) {
return (
<div className="starter-list starter-workbench" aria-label="Jyotisha 推荐的初始问题">
<section className="starter-hero" aria-labelledby="starter-heading">
<div className="starter-hero-copy">
<p className="starter-greeting">{starterGreeting.salutation}</p>
<h1 id="starter-heading">{starterGreeting.question}</h1>
</div>
</section>
<section className="product-entrypoints" aria-label="常用占星入口">
<article className="daily-starlanguage-card product-entrypoint-card" aria-labelledby="daily-starlanguage-title">
<button
className="product-entrypoint-hitarea"
type="button"
aria-label={natalMinuteAvailable ? dailyStarlanguageQuestion : "查看今日运势"}
disabled={productEntrypointsDisabled}
onClick={startDailyStarlanguageConsultation}
/>
<div className="product-entrypoint-copy">
<h2 id="daily-starlanguage-title">{natalMinuteAvailable ? "今日星语" : "每日运势"}</h2>
<p
role={natalMinuteAvailable && dailyStarlanguage.kind !== "ready" ? "status" : undefined}
>{natalMinuteAvailable ? dailyStarlanguageTrend : "看看今天的整体节奏、适合推进的事和需要留意的地方。"}</p>
</div>
<div className="product-entrypoint-footer">
{natalMinuteAvailable
? (dailyStarlanguageAction ? <small>{dailyStarlanguageAction}</small> : null)
: <small>不支持的个人判断会明确说明,不会补造出生时间。</small>}
<span className="product-entrypoint-action" aria-hidden="true">{natalMinuteAvailable ? dailyStarlanguageQuestion : "查看今日运势"} <ArrowUpRight className="starter-arrow" /></span>
</div>
</article>
<article
className="birth-rectification-card product-entrypoint-card"
aria-labelledby="birth-rectification-title"
data-opening={rectificationLoading ? "true" : undefined}
>
<button
className="product-entrypoint-hitarea"
type="button"
aria-label={rectificationCardLabel}
disabled={productEntrypointsDisabled || rectificationLoading || rectificationMutationPending}
onClick={() => void openRectificationFromHomepage()}
/>
<div className="product-entrypoint-copy">
<h2 id="birth-rectification-title">生时校正</h2>
<p>{rectificationEntrySummary?.hasResumableCase
? "新建一段独立校正;未完成的记录仍可从左侧历史会话继续。"
: rectificationCardAction === "restart"
? "上一次校正已经完成,可以基于最新资料再次校正。"
: "不确定准确出生时间时,可通过已经发生的人生事件逐步缩小范围。"}</p>
</div>
<div className="product-entrypoint-footer">
<span className="product-entrypoint-action" aria-hidden="true">{rectificationCardLabel} <ArrowUpRight className="starter-arrow" /></span>
</div>
</article>
</section>
{rectificationError && !rectificationSurfaceOpen && (
<p className="form-error rectification-entry-error" role="alert">
{rectificationErrorMessage}
</p>
)}
<section className="starter-themes" aria-labelledby="starter-themes-heading">
<div className="starter-section-heading">
<h2 id="starter-themes-heading">从一个主题开始</h2>
<p>{natalMinuteAvailable
? "选择一个你现在想解决的问题。"
: "选择一个你现在想解决的问题;出生时间不足以支持的部分,我会明确说明,不会补造具体分钟。"}</p>
</div>
<div className="starter-theme-accordion">
{starterSuggestions.map((item) => {
const theme = starterThemes.find((candidate) => candidate.id === item.theme);
return (
<button
className="starter-theme-card"
key={`${item.theme}-${item.text}`}
type="button"
aria-label={`${theme?.label || "开始"}${item.text}`}
disabled={productEntrypointsDisabled}
onClick={() => void startSuggestedConsultation(item.text, item.theme, "guided_topic")}
>
<span className="starter-content">
<b>{theme?.label || "开始"}</b>
<span>{item.text}</span>
</span>
<ArrowUpRight className="starter-arrow" aria-hidden="true" />
</button>
);
})}
</div>
</section>
{onboardingError && <p className="starter-note">个性化问题暂时不可用,已显示安全的默认问题。</p>}
</div>
);
}