import React from "react";
import {
Box,
List,
ListItem,
ListItemIcon,
ListItemText,
Paper,
Typography,
} from "@mui/material";
import {
CartesianGrid,
Cell,
ResponsiveContainer,
Scatter,
ScatterChart,
Tooltip as RechartsTooltip,
XAxis,
YAxis,
} from "recharts";
import type { FactAnnotationResponse, TimelineEvent } from "../types";
interface TimelineProps {
timeline: TimelineEvent[];
annotations: FactAnnotationResponse[];
}
interface PlotPoint {
x: number;
y: number;
year: number;
label: string;
hasInconsistency: boolean;
color: string;
}
function getEventColor(event: TimelineEvent, ann?: FactAnnotationResponse): string {
if (ann) {
return ann.status === "consistent" ? "#22c55e" : "#ef4444";
}
return event.has_inconsistency ? "#ef4444" : "#94a3b8";
}
// Typed loosely so recharts can inject active/payload via cloneElement
function ScatterTooltip(props: object): React.ReactElement | null {
const { active, payload } = props as {
active?: boolean;
payload?: Array<{ payload: PlotPoint }>;
};
if (!active || !payload?.length) return null;
const pt = payload[0].payload;
return (
{pt.label}
Year: {pt.year}
{pt.hasInconsistency && (
Inconsistency detected
)}
);
}
function Timeline({ timeline, annotations }: TimelineProps): React.ReactElement {
if (timeline.length === 0) {
return (
Temporal Timeline
No temporal events to display.
);
}
const annotationMap = new Map();
annotations.forEach((ann) => {
if (!annotationMap.has(ann.sentence_idx)) {
annotationMap.set(ann.sentence_idx, ann);
}
});
const validEvents = timeline.filter((e) => e.year !== null);
// Fallback list when not enough parseable years for a chart
if (validEvents.length < 2) {
return (
Temporal Timeline
{timeline.map((event, idx) => {
const ann = annotationMap.get(event.sentence_idx);
const color = getEventColor(event, ann);
return (
{event.label}
}
secondary={
{event.year !== null ? `Year: ${event.year}` : "Unknown year"}
}
/>
);
})}
);
}
const plotData: PlotPoint[] = validEvents.map((event, idx) => {
const ann = annotationMap.get(event.sentence_idx);
return {
x: event.year as number,
y: idx,
year: event.year as number,
label: event.label,
hasInconsistency: event.has_inconsistency,
color: getEventColor(event, ann),
};
});
return (
Temporal Timeline
String(v)}
tick={{ fill: "#94a3b8", fontSize: 12 }}
axisLine={{ stroke: "rgba(148,163,184,0.3)" }}
tickLine={{ stroke: "rgba(148,163,184,0.3)" }}
/>
} cursor={false} />
{plotData.map((entry, index) => (
|
))}
{[
{ color: "#22c55e", label: "Consistent" },
{ color: "#ef4444", label: "Inconsistent" },
{ color: "#94a3b8", label: "Unverified" },
].map(({ color, label }) => (
{label}
))}
);
}
export default Timeline;