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>
166 lines
8.1 KiB
TypeScript
166 lines
8.1 KiB
TypeScript
"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>
|
||
|
||
);
|
||
}
|