Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
144 lines
7.8 KiB
TypeScript
144 lines
7.8 KiB
TypeScript
"use client";
|
|
|
|
import dynamic from "next/dynamic";
|
|
import type { FormEvent } from "react";
|
|
import { BirthTimeAssessmentOverlay, type BirthTimeAssessmentPhase } from "@/components/birth-time-assessment-overlay";
|
|
import { BirthLocationFields } from "@/components/birth-location-fields";
|
|
import { BirthTimeIntakeFields } from "@/components/birth-time-intake";
|
|
import { OnboardingChatMessage } from "@/components/onboarding-chat-message";
|
|
import type { BirthTimeGuidedController } from "@/hooks/use-birth-time-guided-journey";
|
|
import {
|
|
applyBirthTimeDraftPatch,
|
|
birthTimeDraftReadyHint,
|
|
isBirthTimeDraftReady,
|
|
} from "@/lib/birth-time-intake-model";
|
|
import type { JourneyClientResponse } from "@/lib/birth-time-journey-client";
|
|
import {
|
|
birthQuestion,
|
|
formatBirthMoment,
|
|
placeQuestion,
|
|
selectedBirthPlace,
|
|
} from "@/lib/home-profile";
|
|
import { presetOnboardingMessage, type OnboardingStep, type Profile } from "@/lib/home-types";
|
|
|
|
const BirthTimeRectification = dynamic(
|
|
() => import("@/components/birth-time-rectification").then((module) => module.BirthTimeRectification),
|
|
{
|
|
ssr: false,
|
|
loading: () => <p className="birth-time-assistant-intent" role="status">正在加载出生时间评估...</p>,
|
|
},
|
|
);
|
|
|
|
export type HomeOnboardingShellProps = {
|
|
readonly profileComplete: boolean;
|
|
readonly profileDraft: Profile;
|
|
readonly setProfileDraft: (update: Profile | ((current: Profile) => Profile)) => void;
|
|
readonly onboardingStep: OnboardingStep;
|
|
readonly presetMessageLength: number;
|
|
readonly currentOnboardingMessage: string;
|
|
readonly onboardingJustCompleted: boolean;
|
|
readonly onboardingCardReady: boolean;
|
|
readonly presetMessageFinished: boolean;
|
|
readonly birthTimeJourney: JourneyClientResponse | null;
|
|
readonly birthTimeAssessmentPhase: BirthTimeAssessmentPhase | null;
|
|
readonly birthTimeError: string;
|
|
readonly birthTimeGuided: BirthTimeGuidedController;
|
|
readonly profileSaving: boolean;
|
|
readonly saveOnboardingBirth: (event: FormEvent<HTMLFormElement>) => void;
|
|
readonly saveOnboardingPlace: (event: FormEvent<HTMLFormElement>) => void;
|
|
readonly retryBirthTimeAssessment: () => void;
|
|
};
|
|
|
|
export function HomeOnboardingShell({
|
|
profileComplete,
|
|
profileDraft,
|
|
setProfileDraft,
|
|
onboardingStep,
|
|
presetMessageLength,
|
|
currentOnboardingMessage,
|
|
onboardingJustCompleted,
|
|
onboardingCardReady,
|
|
presetMessageFinished,
|
|
birthTimeJourney,
|
|
birthTimeAssessmentPhase,
|
|
birthTimeError,
|
|
birthTimeGuided,
|
|
profileSaving,
|
|
saveOnboardingBirth,
|
|
saveOnboardingPlace,
|
|
retryBirthTimeAssessment,
|
|
}: HomeOnboardingShellProps) {
|
|
const birthTimeContinueHint = onboardingStep === "birth" ? birthTimeDraftReadyHint(profileDraft) : "";
|
|
return (
|
|
<>
|
|
<OnboardingChatMessage role="assistant" text={presetOnboardingMessage} streaming={onboardingStep === "name" && !profileComplete} length={presetMessageLength} />
|
|
{(onboardingStep !== "name" || profileComplete) && <OnboardingChatMessage role="user" text={profileDraft.name.trim()} />}
|
|
{(onboardingStep !== "name" || profileComplete) && <OnboardingChatMessage role="assistant" text={birthQuestion(profileDraft.name.trim())} streaming={onboardingStep === "birth" && !profileComplete} length={presetMessageLength} />}
|
|
{(onboardingStep === "place" || onboardingStep === "rectification" || profileComplete) && <OnboardingChatMessage role="user" text={formatBirthMoment(profileDraft)} />}
|
|
{(onboardingStep === "place" || onboardingStep === "rectification" || profileComplete) && <OnboardingChatMessage role="assistant" text={placeQuestion(profileDraft)} streaming={onboardingStep === "place" && !profileComplete} length={presetMessageLength} />}
|
|
{!profileComplete && onboardingStep === "rectification" && selectedBirthPlace(profileDraft) && <OnboardingChatMessage role="user" text={selectedBirthPlace(profileDraft)?.label ?? ""} />}
|
|
{!profileComplete && onboardingStep === "rectification" && birthTimeJourney && <OnboardingChatMessage role="assistant" text={currentOnboardingMessage} streaming length={presetMessageLength} phraseSafe />}
|
|
{profileComplete && onboardingJustCompleted && selectedBirthPlace(profileDraft) && <OnboardingChatMessage role="user" text={selectedBirthPlace(profileDraft)?.label ?? ""} />}
|
|
{profileComplete && onboardingJustCompleted && <OnboardingChatMessage role="assistant" text={currentOnboardingMessage} streaming length={presetMessageLength} />}
|
|
|
|
{!profileComplete && onboardingStep === "birth" && onboardingCardReady && (
|
|
<div className="onboarding-card-reveal">
|
|
<div className="onboarding-card-reveal-inner">
|
|
<form className="profile-form onboarding-card onboarding-step-card birth-time-transition-card" onSubmit={saveOnboardingBirth} aria-busy={birthTimeAssessmentPhase !== null}>
|
|
<div className="onboarding-card-heading">
|
|
<div className="onboarding-card-heading-copy">
|
|
<b>出生日期与时间</b>
|
|
</div>
|
|
</div>
|
|
<fieldset className="birth-time-transition-fields" disabled={birthTimeAssessmentPhase !== null}>
|
|
<BirthTimeIntakeFields value={profileDraft} onPatch={(patch) => setProfileDraft((current) => applyBirthTimeDraftPatch(current, patch))} />
|
|
<div className="onboarding-card-actions">
|
|
{birthTimeContinueHint ? <p className="onboarding-card-action-hint">{birthTimeContinueHint}</p> : <span />}
|
|
<button className="button-primary" type="submit" disabled={profileSaving || !isBirthTimeDraftReady(profileDraft)}>{profileSaving ? "保存中" : "继续"}</button>
|
|
</div>
|
|
</fieldset>
|
|
<BirthTimeAssessmentOverlay phase={birthTimeAssessmentPhase} />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!profileComplete && onboardingStep === "place" && onboardingCardReady && (
|
|
<div className="onboarding-card-reveal">
|
|
<div className="onboarding-card-reveal-inner">
|
|
<form className="profile-form onboarding-card onboarding-step-card birth-time-transition-card" onSubmit={saveOnboardingPlace} aria-busy={birthTimeAssessmentPhase !== null}>
|
|
<div className="onboarding-card-heading"><b>出生地点</b><small>搜索全球城市或区县,系统会匹配出生当日的时区。</small></div>
|
|
<fieldset className="birth-time-transition-fields" disabled={birthTimeAssessmentPhase !== null}>
|
|
<BirthLocationFields value={profileDraft} onChange={setProfileDraft} />
|
|
<div className="onboarding-card-actions"><button className="button-primary" type="submit" disabled={profileSaving || !selectedBirthPlace(profileDraft)}>{profileSaving ? "保存中" : "确定"}</button></div>
|
|
</fieldset>
|
|
<BirthTimeAssessmentOverlay phase={birthTimeAssessmentPhase} />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!profileComplete && onboardingStep === "rectification" && presetMessageFinished && birthTimeJourney && (
|
|
<div className="onboarding-card-reveal">
|
|
<div className="onboarding-card-reveal-inner">
|
|
<BirthTimeRectification
|
|
journey={birthTimeJourney}
|
|
controller={birthTimeGuided}
|
|
externalError={birthTimeError}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!profileComplete && onboardingStep === "rectification" && presetMessageFinished && !birthTimeJourney && (
|
|
<div className="onboarding-card birth-time-retry-card" role="status">
|
|
<b>出生时间尚未完成评估</b>
|
|
<p>{birthTimeError || "资料已经保留,但暂时无法恢复校正进度。系统不会应用未经验证的具体时间。"}</p>
|
|
<button className="button-primary" type="button" disabled={profileSaving} onClick={() => void retryBirthTimeAssessment()}>{profileSaving ? "评估中" : "重新评估"}</button>
|
|
</div>
|
|
)}
|
|
|
|
</>
|
|
);
|
|
}
|