fix(frontend): streamline birth time onboarding

This commit is contained in:
Jesse_Chen
2026-07-22 02:35:31 +08:00
parent 153869fee4
commit d5453681ad
18 changed files with 408 additions and 154 deletions
+62 -31
View File
@@ -55,6 +55,9 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
const source = value.birthTimeSource;
const isConfirmed = value.birthTimeStatus === "confirmed";
const displayState = birthTimeDisplayState(value);
const knowledgeMode = source === "period_only" || source === "unknown"
? "uncertain"
: source ? "exact" : "";
const usesClockTime = source === "hospital_record"
|| source === "family_exact"
|| source === "approximate"
@@ -96,15 +99,19 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
)}
{!isConfirmed && <fieldset className="birth-time-source-fieldset">
<legend></legend>
<legend></legend>
<div className="birth-time-source-list">
{birthTimeSourceOptions.map((option) => (
<label
className={`birth-time-source-option ${source === option.value ? "is-selected" : ""}`}
className={`birth-time-source-option ${option.value === "family_exact"
? knowledgeMode === "exact" ? "is-selected" : ""
: knowledgeMode === "uncertain" ? "is-selected" : ""}`}
key={option.value}
>
<input
checked={source === option.value}
checked={option.value === "family_exact"
? knowledgeMode === "exact"
: knowledgeMode === "uncertain"}
name={`birth-time-source-${groupId}`}
type="radio"
value={option.value}
@@ -139,7 +146,7 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
{source === "hospital_record" && (
<p className="birth-time-detail-note"> 2 D9 / D10 </p>
)}
{(source === "family_exact" || source === "approximate") && (
{source === "approximate" && (
<label>
<span></span>
<select
@@ -149,7 +156,7 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
onPatch({ uncertaintyBeforeMinutes: minutes, uncertaintyAfterMinutes: minutes });
}}
>
{(source === "family_exact" ? [5, 10, 15] : [15, 30, 60]).map((minutes) => (
{[15, 30, 60].map((minutes) => (
<option key={minutes} value={minutes}> {minutes} </option>
))}
</select>
@@ -159,35 +166,59 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
)}
{source === "period_only" && (
<label className="onboarding-card-reveal">
<span></span>
<select
required
value={value.birthTimePeriod}
onChange={(event) => {
const period = birthTimePeriodOptions.find((option) => option.value === event.target.value);
if (period) onPatch({ birthTimePeriod: period.value });
}}
>
<option value="" disabled></option>
{birthTimePeriodOptions.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
</label>
<div className="birth-time-detail-grid onboarding-card-reveal">
<label>
<span></span>
<select
required
value={value.birthTimePeriod}
onChange={(event) => {
const period = birthTimePeriodOptions.find((option) => option.value === event.target.value);
if (period) onPatch({ birthTimePeriod: period.value });
}}
>
<option value="" disabled></option>
{birthTimePeriodOptions.map((option) => (
<option key={option.value} value={option.value}>{option.label}</option>
))}
</select>
</label>
<label>
<span></span>
<textarea
maxLength={240}
placeholder="例如:天刚亮、午饭前后、家人记得大约 6—8 点"
rows={2}
value={value.birthTimeClue}
onChange={(event) => onPatch({ birthTimeClue: event.target.value })}
/>
</label>
<button
className="button-secondary"
type="button"
onClick={() => onPatch({
birthTimeSource: "unknown",
reportedTime: "",
birthTimePeriod: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: null,
uncertaintyAfterMinutes: null,
birthTimeStatus: "reported",
time: "",
})}
></button>
</div>
)}
{source === "unknown" && (
<label className="onboarding-card-reveal">
<span>线</span>
<textarea
maxLength={240}
placeholder="例如:家人只记得天黑以后,或可以再询问长辈"
rows={3}
value={value.birthTimeClue}
onChange={(event) => onPatch({ birthTimeClue: event.target.value })}
/>
</label>
<div className="birth-time-detail-note onboarding-card-reveal" role="status">
<p>使</p>
<button
className="button-secondary"
type="button"
onClick={() => onPatch({ birthTimeSource: "period_only", birthTimeStatus: "reported" })}
></button>
</div>
)}
</div>
);
+1 -3
View File
@@ -1,6 +1,4 @@
import { ChatMessageContent } from "@/components/chat-message-content";
import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge";
import { EvidenceAuditPanel } from "@/components/evidence-audit-panel";
import type { ChatMessageView } from "@/lib/chat-message-view";
import { consultationReportMarkdown, downloadMarkdownReport } from "@/lib/consultation-report-export";
@@ -26,7 +24,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView
{message.role === "assistant" ? (
message.state === "thinking"
? <div className="thinking"><i /><i /><i /></div>
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} workflowReceipt={message.workflowReceipt} /><ChatMessageContent text={message.text} /><button className="report-download-button" type="button" onClick={() => downloadMarkdownReport("Jyotisha 咨询报告", consultationReportMarkdown({ title: "Jyotisha 咨询报告", messages: [message] }))}></button></>
: <><ChatMessageContent text={message.text} /><button className="report-download-button" type="button" onClick={() => downloadMarkdownReport("Jyotisha 咨询报告", consultationReportMarkdown({ title: "Jyotisha 咨询报告", messages: [message] }))}></button></>
) : <p>{message.text}</p>}
</div>
</div>