"use client"; import { Round, Participant, Personality } from "@/lib/types"; import { PERSONALITY_LABELS } from "@/lib/utils"; interface Props { rounds: Round[]; participants: Participant[]; } function Icon({ name, className = "" }: { name: string; className?: string }) { return {name}; } const ACTION_ICON: Record = { propose: "send", counter: "swap_horiz", accept: "check_circle", escalate: "warning", }; const ACTION_LABEL: Record = { propose: "Proposed", counter: "Counter", accept: "Accepted", escalate: "Escalated", }; const ACTION_BADGE: Record = { propose: "text-[#B7A6FB] bg-[#B7A6FB]/10 border-[#B7A6FB]/20", counter: "text-slate-300 bg-white/5 border-white/10", accept: "text-emerald-400 bg-emerald-500/10 border-emerald-500/20", escalate: "text-amber-400 bg-amber-500/10 border-amber-500/20", }; export default function NegotiationTimeline({ rounds, participants }: Props) { if (!rounds || rounds.length === 0) { return ( Negotiation hasn't started yet ); } const userA = participants?.[0]; const userB = participants?.[1]; function agentLabel(proposerId: number) { if (userA && proposerId === userA.user_id) return "A"; if (userB && proposerId === userB.user_id) return "B"; return "?"; } function agentPersonality(proposerId: number): Personality { const p = participants?.find((p) => p.user_id === proposerId); return (p?.personality_used ?? "balanced") as Personality; } return ( {/* Vertical data-stream line */} {rounds.map((round, idx) => { const label = agentLabel(round.proposer_id); const personality = agentPersonality(round.proposer_id); const action = round.response_type ?? "propose"; const isA = label === "A"; const isLast = idx === rounds.length - 1; return ( {/* Node */} {String(round.round_number).padStart(2, "0")} {/* Content */} Agent {label} {ACTION_LABEL[action] ?? action} {PERSONALITY_LABELS[personality]} {round.reasoning && ( {round.reasoning} )} {round.proposal && typeof round.proposal === "object" && ( } /> )} {/* Satisfaction */} Sat A = 70 ? "text-[#B7A6FB]" : "text-red-400"}`}> {round.satisfaction_a?.toFixed(0) ?? "—"}% Sat B = 70 ? "text-cyan-400" : "text-red-400"}`}> {round.satisfaction_b?.toFixed(0) ?? "—"}% {round.concessions_made?.length > 0 && ( Concession )} ); })} ); } function ProposalSnippet({ proposal }: { proposal: Record }) { const summary = (proposal.summary as string) ?? (proposal.for_party_a as string) ?? null; if (!summary) return null; return ( {summary} ); }
{round.reasoning}