Files
Jyotisha/frontend/src/components/rectification-segment-delivery.tsx
T
Jesse_ChenandClaude Opus 5.5 6987875b84
Independent Staging Quality Gate / validate (push) Successful in 14m56s
Independent Staging Quality Gate / publish (push) Successful in 3m23s
feat(rectification): post-adopt verdict says whether the adopted time still fits and offers 改用 HH:MM; renumber BUG-1170→1172 (BUG-1173)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-02 07:40:30 +08:00

80 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { SIGN_LABELS, TIER_LABELS, type SegmentSummary } from "@/lib/rectification-agentic/core/segment-summary";
import type { RangeDeliveryProjection } from "@/lib/rectification-agentic/v9/divergence-panel";
import { RECTIFICATION_USER_COPY, REPRESENTATIVE_MINUTE_DISCLAIMER } from "@/lib/rectification-agentic/user-copy";
import { SWITCH_TO_RECTIFIED_LABEL } from "@/lib/rectification-agentic/birth-time-provenance";
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
import { protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
import { unknownProductDeliveryCopy } from "@/lib/rectification-agentic/v9/product-delivery";
export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, selection, resultId, busy, accepting, onAccept, adoptLocked, markdown, candidateDate }: Readonly<{
summary: SegmentSummary | null;
delivery: RangeDeliveryProjection;
rangeLabel: string | null;
selection?: Readonly<{ resultId: string; candidateId: string; date: string; time: string; contract: "segment-v1" }> | null;
resultId: string;
busy: boolean;
accepting: boolean;
onAccept: (candidateId: string) => void;
adoptLocked?: boolean;
markdown?: string | null;
candidateDate?: string;
}>) {
const adoption = summary?.adoption_minute;
const candidateId = delivery.representative_candidate_id;
const exactAdoption = Boolean(adoption?.date && selection?.contract === "segment-v1" && selection.resultId === resultId
&& selection.candidateId === candidateId && selection.date === adoption.date && selection.time === adoption.time);
// BUG-1173: a post-adopt answer can move the server's adoption minute. Only when the adopted
// minute left the verdict (server `readopt_minute`) does the button come back, as 「改用 HH:MM」.
const readopt = delivery.readopt_minute ?? null;
const priorAdoption = selection?.contract === "segment-v1" && selection.resultId === resultId ? selection : null;
const adopted = exactAdoption || Boolean(priorAdoption && !readopt);
const charts = summary?.charts.filter((chart) => chart.tier !== "blocked") ?? [];
const blocked = summary?.charts.some((chart) => chart.tier === "blocked") === true;
const reportBlocked = !summary || blocked;
return (
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正盘型">
<div className="rectification-candidates-heading"><strong>用哪张盘解读</strong></div>
<ul className="rectification-range-delivery__shared">
{charts.map((chart) => (
<li key={chart.chart}>
{chart.chart} 上升:{chart.sign === null ? "分不开" : SIGN_LABELS[chart.sign]}
({TIER_LABELS[chart.tier]}{chart.tied ? ",头段并列" : ""})
{chart.tier !== "certain" && chart.alternatives.length > 0
? `,备选:${chart.alternatives.map((sign) => SIGN_LABELS[sign]).join("、")}` : ""}
</li>
))}
</ul>
{blocked ? <p className="rectification-range-delivery__narrow">分盘解读需要更准的出生时间</p> : null}
{rangeLabel ? <p className="rectification-range-delivery__muted">分钟范围 {rangeLabel}</p> : null}
{!summary && delivery.product_delivery ? <p className="rectification-range-delivery__indistinct">{unknownProductDeliveryCopy(delivery.product_delivery)}</p> : null}
{summary && !summary.scan_complete ? <p className="rectification-range-delivery__muted">完整窗口扫描暂不可用;候选盘型不能证明全窗一致。{candidateDate ? `候选代表日期 ${candidateDate};原填报日期保留。` : ""}</p> : null}
{adoptLocked ? <p className="rectification-range-delivery__narrow">{RECTIFICATION_USER_COPY.rangeDeliveryAdoptLocked}</p> : null}
{delivery.provenance_line ? <p className="rectification-range-delivery__provenance">{delivery.provenance_line}</p> : null}
{adoption && candidateId ? (
<>
<p className="rectification-range-delivery__muted">采用后按 {adoption.date ? `${adoption.date} ` : ""}{adoption.time} 排盘;原填报资料保留。</p>
<button type="button" className="rectification-range-delivery__accept" disabled={busy || adoptLocked || adopted}
aria-pressed={adopted} onClick={() => onAccept(candidateId)}>
{accepting ? RECTIFICATION_USER_COPY.rangeDeliveryAdopting : adopted ? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: readopt && priorAdoption ? `改用 ${readopt}`
: delivery.record_conflict ? SWITCH_TO_RECTIFIED_LABEL : `改用 ${charts.map((chart) => chart.chart).join(" + ")} 盘解读`}
</button>
{readopt && priorAdoption && !adopted ? (
<p className="rectification-range-delivery__switch-note">现在按 {priorAdoption.time} 排盘;改用后按 {readopt} 排盘,原填报资料保留。</p>
) : null}
{delivery.record_conflict && !adopted ? (
<p className="rectification-range-delivery__switch-note">
选它之后,排盘会从出生记录时间 {delivery.record_conflict.reported_time} 换成 {adoption.date ? `${adoption.date} ` : ""}{adoption.time}。
</p>
) : null}
</>
) : null}
<p className="rectification-range-delivery__boundary">{delivery.boundary || REPRESENTATIVE_MINUTE_DISCLAIMER}</p>
<RectificationVerificationReport markdown={protectVargaReport(markdown === undefined ? delivery.verification_markdown : markdown, reportBlocked, delivery.product_delivery?.targets ?? summary?.targets,
!summary ? "完整窗口扫描暂不可用;不能提供分盘解读推荐。" : undefined)} />
</section>
);
}