宽度超过 10 分钟或头名并列时不再出交付卡,改为按大运边界逐条问、 用类型芯片和年/月选择器录入。跳过的线换问法再问一次;答「这类事 都没有过」的不再问。用户说「没有了」仍立刻给目前范围。Skill 10.0.27。 BUG-740~743
147 lines
5.9 KiB
TypeScript
147 lines
5.9 KiB
TypeScript
"use client";
|
||
|
||
import type { RectificationCandidateResult } from "@/lib/rectification-candidate-result";
|
||
import {
|
||
RECTIFICATION_USER_COPY,
|
||
REPRESENTATIVE_MINUTE_DISCLAIMER,
|
||
rangeDeliveryEventCopy,
|
||
} from "@/lib/rectification-agentic/user-copy";
|
||
import type { RangeDeliveryProjection } from "@/lib/rectification-agentic/v9/divergence-panel";
|
||
import {
|
||
SWITCH_TO_RECTIFIED_LABEL,
|
||
switchToRectifiedConfirmCopy,
|
||
} from "@/lib/rectification-agentic/birth-time-provenance";
|
||
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
|
||
|
||
function columnHasTraits(column: RangeDeliveryProjection["columns"][number]): boolean {
|
||
return Boolean(column.traits.d9 || column.traits.d10 || column.traits.nakshatra.length > 0);
|
||
}
|
||
|
||
export function RectificationRangeDelivery({
|
||
result,
|
||
acceptingCandidateId,
|
||
readonly,
|
||
onAccept,
|
||
adoptLocked,
|
||
}: Readonly<{
|
||
result: RectificationCandidateResult;
|
||
acceptingCandidateId: string | null;
|
||
readonly: boolean;
|
||
onAccept: (candidateId: string) => void;
|
||
adoptLocked?: boolean;
|
||
}>) {
|
||
const delivery: RangeDeliveryProjection | null = result.rangeDelivery;
|
||
const range = delivery?.range ?? result.credibleRange;
|
||
const eventCount = delivery?.event_count ?? 0;
|
||
const selected = result.selectedTime;
|
||
const busy = Boolean(acceptingCandidateId) || readonly || adoptLocked;
|
||
const columns = delivery?.columns ?? [];
|
||
const title = range
|
||
? eventCount > 0
|
||
? `${RECTIFICATION_USER_COPY.rangeDeliveryTitle} ${range[0]}–${range[1]}(${rangeDeliveryEventCopy(eventCount)})`
|
||
: `${RECTIFICATION_USER_COPY.rangeDeliveryTitle} ${range[0]}–${range[1]}`
|
||
: RECTIFICATION_USER_COPY.rangeDeliveryTitle;
|
||
const markdown = delivery?.verification_markdown
|
||
?? result.verificationReportMarkdown;
|
||
const sharedTraits = delivery?.shared_traits ?? [];
|
||
// BUG-691: while the birth record disagrees with the range the adopt entry says what
|
||
// it actually does — swap the charting clock — instead of the neutral 「更像这个」.
|
||
const recordConflict = delivery?.record_conflict ?? null;
|
||
const caption = delivery?.narrow_hint ?? null;
|
||
|
||
return (
|
||
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正区间">
|
||
<div className="rectification-candidates-heading">
|
||
<strong>{title}</strong>
|
||
</div>
|
||
{caption ? (
|
||
<p className="rectification-range-delivery__narrow">{caption}</p>
|
||
) : null}
|
||
{delivery?.provenance_line ? (
|
||
<p className="rectification-range-delivery__provenance">{delivery.provenance_line}</p>
|
||
) : null}
|
||
{adoptLocked ? (
|
||
<p className="rectification-range-delivery__narrow">
|
||
{RECTIFICATION_USER_COPY.rangeDeliveryAdoptLocked}
|
||
</p>
|
||
) : null}
|
||
{delivery?.tie_break_note ? (
|
||
<p className="rectification-range-delivery__narrow">{delivery.tie_break_note}</p>
|
||
) : null}
|
||
{sharedTraits.length > 0 ? (
|
||
<ul className="rectification-range-delivery__shared">
|
||
{sharedTraits.map((line) => (
|
||
<li key={line}>{line}</li>
|
||
))}
|
||
</ul>
|
||
) : null}
|
||
<ul className="rectification-range-delivery__columns">
|
||
{columns.map((column) => {
|
||
const adopted = selected === column.time;
|
||
const adopting = acceptingCandidateId === column.candidate_id;
|
||
const missing = column.fit == null || column.windows == null;
|
||
return (
|
||
<li
|
||
key={column.candidate_id}
|
||
className={`rectification-range-delivery__column${adopted ? " is-adopted" : ""}`}
|
||
>
|
||
<div className="rectification-range-delivery__time">{column.time}</div>
|
||
<p className="rectification-range-delivery__likelihood">
|
||
{RECTIFICATION_USER_COPY.rangeDeliveryRelativeLikelihood}
|
||
{" "}
|
||
{column.probability_percent}%
|
||
</p>
|
||
{columnHasTraits(column) ? (
|
||
<div className="rectification-range-delivery__block">
|
||
{column.traits.d9 ? <p>{column.traits.d9}</p> : null}
|
||
{column.traits.d10 ? <p>{column.traits.d10}</p> : null}
|
||
{column.traits.nakshatra.map((line) => (
|
||
<p key={line}>{line}</p>
|
||
))}
|
||
</div>
|
||
) : null}
|
||
{missing ? (
|
||
<p className="rectification-range-delivery__muted">
|
||
{RECTIFICATION_USER_COPY.rangeDeliveryNotCompared}
|
||
</p>
|
||
) : (
|
||
<>
|
||
<p>{column.fit_line}</p>
|
||
<p>{column.window_line}</p>
|
||
</>
|
||
)}
|
||
<button
|
||
type="button"
|
||
className="rectification-range-delivery__accept"
|
||
aria-pressed={adopted}
|
||
disabled={busy || adopted}
|
||
onClick={() => onAccept(column.candidate_id)}
|
||
>
|
||
{adopting
|
||
? RECTIFICATION_USER_COPY.rangeDeliveryAdopting
|
||
: adopted
|
||
? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
|
||
: recordConflict
|
||
? SWITCH_TO_RECTIFIED_LABEL
|
||
: RECTIFICATION_USER_COPY.rangeDeliveryMoreLikeThis}
|
||
</button>
|
||
{recordConflict && !adopted ? (
|
||
<p className="rectification-range-delivery__switch-note">
|
||
{switchToRectifiedConfirmCopy(recordConflict.reported_time, column.time)}
|
||
</p>
|
||
) : null}
|
||
</li>
|
||
);
|
||
})}
|
||
</ul>
|
||
{delivery?.more_label ? (
|
||
<p className="rectification-range-delivery__more">{delivery.more_label}</p>
|
||
) : null}
|
||
<p className="rectification-range-delivery__boundary">
|
||
{delivery?.boundary ?? REPRESENTATIVE_MINUTE_DISCLAIMER}
|
||
</p>
|
||
<RectificationVerificationReport markdown={markdown} />
|
||
</section>
|
||
);
|
||
}
|