fix: harden birth-time consultation modes

This commit is contained in:
Jesse_Chen
2026-07-21 07:25:02 +08:00
parent fc1e9f1085
commit 8be39d5ad6
21 changed files with 1516 additions and 206 deletions
@@ -361,7 +361,9 @@ export function ConversationalRectificationSurface({
{controller.error && <p className="form-error" role="alert">{controller.error}</p>}
</div>
{turn.actions.includes("continue_original_question") && pendingQuestion && (
{turn.actions.includes("continue_original_question")
&& pendingQuestion
&& onContinueOriginalQuestion && (
<section className="conversational-original-question">
<p>{pendingQuestion}</p>
<button
@@ -457,15 +459,22 @@ type ConversationalBirthTimeRectificationProps = Readonly<{
initialTurn?: ConversationalRectificationTurn | null;
pendingConsultationQuestion?: string | null;
onTurn?: (turn: ConversationalRectificationTurn) => void;
onPendingChange?: (pending: boolean) => void;
onContinueOriginalQuestion?: (question: string) => void;
}>;
export function ConversationalBirthTimeRectification(
props: ConversationalBirthTimeRectificationProps,
) {
const pendingChange = useRef(props.onPendingChange);
useEffect(() => {
pendingChange.current = props.onPendingChange;
}, [props.onPendingChange]);
useEffect(() => () => pendingChange.current?.(false), []);
const controller = useConversationalRectification({
initialTurn: props.initialTurn,
onTurn: props.onTurn,
onPendingChange: props.onPendingChange,
});
return (
<ConversationalRectificationSurface
@@ -1,12 +1,14 @@
"use client";
import { useId } from "react";
import { useEffect, useId, useRef, type KeyboardEvent } from "react";
import { keepFocusWithin } from "@/lib/focus-trap";
type UnverifiedBirthTimeChoiceProps = Readonly<{
canUseUnverifiedTime: boolean;
unverifiedTime?: string | null;
pending?: boolean;
onUseUnverifiedTime: () => void;
onContinueGenerally: () => void;
onRectifyFirst: () => void;
onCancel: () => void;
}>;
@@ -16,60 +18,94 @@ export function UnverifiedBirthTimeChoice({
unverifiedTime,
pending = false,
onUseUnverifiedTime,
onContinueGenerally,
onRectifyFirst,
onCancel,
}: UnverifiedBirthTimeChoiceProps) {
const titleId = useId();
const descriptionId = useId();
const dialog = useRef<HTMLElement>(null);
const initialAction = useRef<HTMLButtonElement>(null);
useEffect(() => {
const frame = window.requestAnimationFrame(() => initialAction.current?.focus());
return () => window.cancelAnimationFrame(frame);
}, []);
function handleDialogKeyDown(event: KeyboardEvent<HTMLElement>) {
if (event.key === "Escape") {
event.preventDefault();
if (!pending) onCancel();
return;
}
const container = dialog.current;
if (container) keepFocusWithin(event.nativeEvent, container);
}
return (
<section
aria-busy={pending}
aria-describedby={descriptionId}
aria-labelledby={titleId}
aria-live="polite"
className="onboarding-card birth-time-transition-card"
role="dialog"
>
<div className="onboarding-card-heading">
<b id={titleId}></b>
<small>使 Jyotisha</small>
</div>
<p id={descriptionId}>
{canUseUnverifiedTime
? "你可以只在当前聊天临时使用填报时间,也可以先完成生时校正再问。新建聊天后会再次温和提醒。"
: "你目前没有可直接使用的具体分钟,系统不会替你猜一个时间。可以先校正再继续这个问题。"}
</p>
<div className="onboarding-card-actions">
{canUseUnverifiedTime && (
<div className="unverified-birth-time-choice-scrim">
<section
aria-busy={pending}
aria-describedby={descriptionId}
aria-labelledby={titleId}
aria-live="polite"
aria-modal="true"
className="onboarding-card birth-time-transition-card unverified-birth-time-choice"
onKeyDown={handleDialogKeyDown}
ref={dialog}
role="alertdialog"
>
<div className="onboarding-card-heading">
<b id={titleId}></b>
<small>使 Jyotisha</small>
</div>
<p id={descriptionId}>
{canUseUnverifiedTime
? "你可以只在当前聊天临时使用填报时间,也可以先完成生时校正再问。新建聊天后会再次温和提醒。"
: "你目前没有可直接使用的具体分钟,系统不会替你猜一个时间。可以先校正,或改问不依赖出生分钟的一般问题。"}
</p>
<div className="onboarding-card-actions">
{canUseUnverifiedTime ? (
<button
className="button-secondary"
disabled={pending}
ref={initialAction}
type="button"
onClick={onUseUnverifiedTime}
>
{unverifiedTime
? `先用 ${unverifiedTime}(未校正)询问`
: "先用未校正时间询问"}
</button>
) : (
<button
className="button-secondary"
disabled={pending}
ref={initialAction}
type="button"
onClick={onContinueGenerally}
>
</button>
)}
<button
className="button-primary"
disabled={pending}
type="button"
onClick={onRectifyFirst}
>
{pending ? "正在进入生时校正…" : "先校正再询问"}
</button>
<button
className="button-secondary"
disabled={pending}
type="button"
onClick={onUseUnverifiedTime}
onClick={onCancel}
>
{unverifiedTime
? `先用 ${unverifiedTime}(未校正)询问`
: "先用未校正时间询问"}
</button>
)}
<button
className="button-primary"
disabled={pending}
type="button"
onClick={onRectifyFirst}
>
{pending ? "正在进入生时校正…" : "先校正再询问"}
</button>
<button
className="button-secondary"
disabled={pending}
type="button"
onClick={onCancel}
>
</button>
</div>
</section>
</div>
</section>
</div>
);
}