fix(rectification): reset fresh cases to reported time

This commit is contained in:
Jesse_Chen
2026-08-15 17:50:51 +08:00
parent b0a1174eae
commit 075c62e579
17 changed files with 878 additions and 256 deletions
+6 -132
View File
@@ -6,11 +6,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { birthTimeConsultationOptionsCopy } from "@/lib/birth-time-consultation-consent";
import {
birthTimeDisplayState,
birthTimePeriodOptions,
birthTimeSourceDefaults,
birthTimeSourceOptions,
type BirthTimeDraft,
type BirthTimeDraftPatch,
type BirthTimeSource,
} from "@/lib/birth-time-intake-model";
type BirthTimeIntakeProps = {
@@ -66,47 +65,11 @@ function BirthClockSelect({ value, onChange }: BirthClockSelectProps) {
);
}
const sourceDefaults = {
hospital_record: {
birthTimePeriod: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: 2,
uncertaintyAfterMinutes: 2,
},
family_exact: {
birthTimePeriod: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: 10,
uncertaintyAfterMinutes: 10,
},
approximate: {
birthTimePeriod: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: 30,
uncertaintyAfterMinutes: 30,
},
period_only: {
reportedTime: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: null,
uncertaintyAfterMinutes: null,
},
unknown: {
reportedTime: "",
birthTimePeriod: "",
uncertaintyBeforeMinutes: null,
uncertaintyAfterMinutes: null,
},
} as const satisfies Record<Exclude<BirthTimeSource, "" | "legacy_import">, BirthTimeDraftPatch>;
export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps) {
const groupId = useId();
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"
@@ -151,20 +114,16 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
)}
{!isConfirmed && <fieldset className="birth-time-source-fieldset">
<legend></legend>
<p className="birth-time-source-intro"></p>
<legend></legend>
<p className="birth-time-source-intro"></p>
<div className="birth-time-source-list">
{birthTimeSourceOptions.map((option) => (
<label
className={`birth-time-source-option ${option.value === "family_exact"
? knowledgeMode === "exact" ? "is-selected" : ""
: knowledgeMode === "uncertain" ? "is-selected" : ""}`}
className={`birth-time-source-option ${source === option.value ? "is-selected" : ""}`}
key={option.value}
>
<input
checked={option.value === "family_exact"
? knowledgeMode === "exact"
: knowledgeMode === "uncertain"}
checked={source === option.value}
name={`birth-time-source-${groupId}`}
type="radio"
value={option.value}
@@ -172,7 +131,7 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
birthTimeSource: option.value,
birthTimeStatus: "reported",
time: "",
...sourceDefaults[option.value],
...birthTimeSourceDefaults[option.value],
})}
/>
<span>
@@ -197,91 +156,6 @@ export function BirthTimeIntakeFields({ value, onPatch }: BirthTimeIntakeProps)
{source === "hospital_record" && (
<p className="birth-time-detail-note"> 2 D9 / D10 </p>
)}
{source === "approximate" && (
<label>
<span></span>
<Select
value={value.uncertaintyBeforeMinutes?.toString() ?? ""}
onValueChange={(nextValue) => {
if (nextValue === null) return;
const minutes = Number(nextValue);
onPatch({ uncertaintyBeforeMinutes: minutes, uncertaintyAfterMinutes: minutes });
}}
>
<SelectTrigger aria-label="可能误差">
<SelectValue placeholder="选择误差范围">
{(selectedValue) => selectedValue ? `前后 ${selectedValue} 分钟` : "选择误差范围"}
</SelectValue>
</SelectTrigger>
<SelectContent>
{[15, 30, 60].map((minutes) => (
<SelectItem key={minutes} value={minutes.toString()}> {minutes} </SelectItem>
))}
</SelectContent>
</Select>
</label>
)}
</div>
)}
{source === "period_only" && (
<div className="birth-time-detail-grid birth-time-period-details onboarding-card-reveal">
<label>
<span></span>
<Select
required
value={value.birthTimePeriod || null}
onValueChange={(nextValue) => {
if (typeof nextValue === "string") onPatch({ birthTimePeriod: nextValue });
}}
>
<SelectTrigger aria-label="最接近的时间范围">
<SelectValue placeholder="请选择大致时段">
{(selectedValue) => birthTimePeriodOptions.find((option) => option.value === selectedValue)?.label ?? "请选择大致时段"}
</SelectValue>
</SelectTrigger>
<SelectContent>
{birthTimePeriodOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
))}
</SelectContent>
</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 birth-time-skip-button"
type="button"
onClick={() => onPatch({
birthTimeSource: "unknown",
reportedTime: "",
birthTimePeriod: "",
birthTimeClue: "",
uncertaintyBeforeMinutes: null,
uncertaintyAfterMinutes: null,
birthTimeStatus: "reported",
time: "",
})}
></button>
</div>
)}
{source === "unknown" && (
<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>
@@ -36,6 +36,11 @@ type PersistedTurn = Readonly<{
receipt?: Readonly<{
status: string;
phases: readonly string[];
tool_activities?: readonly Readonly<{
tool: string;
status: string;
methods?: readonly string[];
}>[];
tools: readonly string[];
methods?: readonly string[];
skill_name?: string;
@@ -94,6 +99,21 @@ const ACTIVE_TOOL_LABELS: Readonly<Record<PublicRectificationTool, string>> = {
function completedReceiptFromPersisted(receipt: PersistedTurn["receipt"]): CompletedActivityReceiptView {
if (!receipt) return { steps: [], methods: [] };
if (Array.isArray(receipt.tool_activities)) {
let state = createRectificationActivityReceiptState();
for (const activity of receipt.tool_activities) {
if (!isPublicRectificationTool(activity.tool)
|| (activity.status !== "completed" && activity.status !== "failed")) continue;
state = reduceRectificationActivityReceipt(state, {
tool: activity.tool,
status: activity.status,
methods: activity.status === "completed" && Array.isArray(activity.methods)
? activity.methods.filter(isPublicRectificationMethod)
: [],
});
}
return receiptFromRectificationActivityState(state);
}
return {
steps: [...new Set((receipt.tools ?? []).filter(isPublicRectificationTool))],
methods: [...new Set((receipt.methods ?? []).filter(isPublicRectificationMethod))],