Files
Jyotisha/frontend/src/components/rectification-range-delivery.tsx
T
Jesse_ChenandCursor 530f260f76
Independent Staging Quality Gate / validate (push) Successful in 12m9s
Independent Staging Quality Gate / publish (push) Successful in 2m17s
fix(rectification): ask remaining split lines one card at a time (BUG-661~663)
Dated-pool empty now yields existence cards per remaining layer, leftover-candidate copy, and an optional D9/D10 tie-break on the range card. Skill 10.0.25.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 16:50:25 +08:00

131 lines
5.0 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 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 { 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,
onTieBreak,
}: Readonly<{
result: RectificationCandidateResult;
acceptingCandidateId: string | null;
readonly: boolean;
onAccept: (candidateId: string) => void;
onTieBreak?: () => void;
}>) {
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;
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 ?? [];
return (
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正区间">
<div className="rectification-candidates-heading">
<strong>{title}</strong>
</div>
{delivery?.narrow_hint ? (
<p className="rectification-range-delivery__narrow">{delivery.narrow_hint}</p>
) : null}
{delivery?.tie_break_available && onTieBreak && !readonly ? (
<button
type="button"
className="rectification-range-delivery__tie-break"
disabled={busy}
onClick={onTieBreak}
>
{RECTIFICATION_USER_COPY.rangeDeliveryTieBreakEntry}
</button>
) : 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
: RECTIFICATION_USER_COPY.rangeDeliveryMoreLikeThis}
</button>
</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>
);
}