Files
Jyotisha/frontend/src/components/rectification-in-chat-cards.tsx
T
Jesse_ChenandClaude Opus 5.5 6ad0bea320 feat(rectification): start from an offer card in the consultation, come back to it (BUG-1247~1251)
TASK-rectification-in-chat-step1-20261006, direct execution.

- The homepage 生时校正 pill and the text trigger (isRectificationHandoffQuestion)
  are gone. The consult agent decides (RECTIFICATION_OFFER_RULE, one
  definition) and calls offer-birth-time-rectification; the server decides
  whether a card may show (self, product on, no case in progress,
  feature_pricing price). The offer is stored with the answer and travels on
  run.completed; the card hangs under that answer.
- Opening from the card binds the offer to the case in the consultation's own
  messages (no table change); GET/POST /api/rectification/cases/[caseId]/source
  find the source and go back: one divider (time changed or not), earlier
  answers captioned 按校正前的时间, and a 按新时间重新看 button that sends only
  when clicked. Auto-return after the post-adopt check or a terminal case, only
  for work done in this view.
- Model history skips the divider and notes answers written with the old time.

Full suite 4987 / fail 24, identical to 6ec8efd5 (4973 / 24), no test name
lost; tsc 0; lint 0 errors; build keeps / Static, first-load gzip +0.20%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-07 12:58:58 +08:00

66 lines
2.5 KiB
TypeScript

"use client";
import { Button } from "@/components/ui/button";
import {
RECTIFICATION_BEFORE_CHANGE_LABEL,
RECTIFICATION_OFFER_CONTINUE_LABEL,
RECTIFICATION_OFFER_OPENED_COPY,
RECTIFICATION_OFFER_START_LABEL,
rectificationOfferCardCopy,
rectificationReturnDividerText,
rectificationRevisitLabel,
type RectificationOfferRecord,
type RectificationReturnRecord,
} from "@/lib/rectification-in-chat";
/**
* The card under a consultation answer that offered birth-time rectification
* (BUG-1248). The server decided it may be shown and wrote the price; the
* click opens the case (the charge comes with the first answered question).
* A card whose case is open says so and offers to go back in; a card whose
* case has returned is gone (the divider below says what happened).
*/
export function RectificationOfferCard({
offer,
disabled,
onStart,
}: Readonly<{ offer: RectificationOfferRecord; disabled: boolean; onStart: () => void }>) {
if (offer.status === "returned") return null;
const opened = offer.status === "opened";
return (
<div className="rectification-offer-card" role="group" aria-label="核对出生时间">
<p>{opened ? RECTIFICATION_OFFER_OPENED_COPY : rectificationOfferCardCopy(offer)}</p>
<Button type="button" variant={opened ? "outline" : "default"} size="sm" disabled={disabled} onClick={onStart}>
{opened ? RECTIFICATION_OFFER_CONTINUE_LABEL : RECTIFICATION_OFFER_START_LABEL}
</Button>
</div>
);
}
/**
* Where the reader came back from rectification (BUG-1250). Re-asking with the
* new time is a button and an ordinary consultation turn: nothing is sent, and
* nothing is charged, until it is clicked.
*/
export function RectificationReturnDivider({
record,
disabled,
onRevisit,
}: Readonly<{ record: RectificationReturnRecord; disabled: boolean; onRevisit: (question: string) => void }>) {
return (
<div className="rectification-return-divider" role="note">
<p className="rectification-return-divider__line">{rectificationReturnDividerText(record)}</p>
{record.adopted ? (
<Button type="button" variant="outline" size="sm" disabled={disabled} onClick={() => onRevisit(record.sourceQuestion)}>
{rectificationRevisitLabel(record.sourceQuestion)}
</Button>
) : null}
</div>
);
}
/** The small line above an answer written with the birth time before the change (P2). */
export function BeforeBirthTimeChangeLabel() {
return <p className="message-before-change">{RECTIFICATION_BEFORE_CHANGE_LABEL}</p>;
}