uaide-backend / src /components /TemporalTimeline.jsx
ATS-27's picture
Upload folder using huggingface_hub
af980d7 verified
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Film, AlertTriangle, CheckCircle, Clock } from 'lucide-react';
import styles from './TemporalTimeline.module.css';
const SEV_LABELS = { critical: 'Critical', high: 'High', medium: 'Medium' };
export default function TemporalTimeline({ timeline, duration = 23, totalFrames }) {
const [hovered, setHovered] = useState(null);
const durationSec = parseFloat(duration) || 23;
const toPercent = (sec) => (sec / durationSec) * 100;
const hasFlags = timeline.flaggedSegments.length > 0;
return (
<div className={styles.wrapper}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<Film size={16} />
<span>Temporal Analysis Timeline</span>
</div>
<div className={styles.headerStats}>
<span className={styles.statChip}>
<span className={styles.chipDot} style={{ background: 'var(--ai-generated)' }} />
{timeline.flaggedSegments.length} flagged segments
</span>
<span className={styles.statChip}>
<span className={styles.chipDot} style={{ background: 'var(--authentic)' }} />
{timeline.cleanSegments.length} clean
</span>
</div>
</div>
{/* Main timeline bar */}
<div className={styles.timelineWrap}>
<div className={styles.timelineBar}>
{/* Clean segments */}
{timeline.cleanSegments.map((seg, i) => (
<div
key={i}
className={styles.segClean}
style={{
left: `${toPercent(seg.start)}%`,
width: `${toPercent(seg.end - seg.start)}%`,
}}
/>
))}
{/* Flagged segments */}
{timeline.flaggedSegments.map((seg, i) => (
<motion.div
key={i}
className={`${styles.segFlagged} ${styles['sev_' + seg.severity]}`}
style={{
left: `${toPercent(seg.start)}%`,
width: `${toPercent(seg.end - seg.start)}%`,
}}
initial={{ opacity: 0, scaleY: 0 }}
animate={{ opacity: 1, scaleY: 1 }}
transition={{ duration: 0.4, delay: i * 0.15 }}
onMouseEnter={() => setHovered(i)}
onMouseLeave={() => setHovered(null)}
>
{/* Tooltip */}
{hovered === i && (
<motion.div
className={styles.tooltip}
initial={{ opacity: 0, y: 4 }}
animate={{ opacity: 1, y: 0 }}
>
<p className={styles.tooltipTitle}>{seg.reason}</p>
<p className={styles.tooltipSub}>
{seg.start.toFixed(1)}s – {seg.end.toFixed(1)}s · {seg.frames.length} frames
</p>
<span className={`${styles.tooltipBadge} ${styles['badgeSev_' + seg.severity]}`}>
{SEV_LABELS[seg.severity]}
</span>
</motion.div>
)}
</motion.div>
))}
</div>
{/* Timestamp rulers */}
<div className={styles.rulers}>
{Array.from({ length: Math.ceil(durationSec / 5) + 1 }, (_, i) => i * 5).map(t => (
t <= durationSec && (
<div key={t} className={styles.rulerMark} style={{ left: `${toPercent(t)}%` }}>
<div className={styles.rulerLine} />
<span className={`${styles.rulerLabel} font-mono`}>{t}s</span>
</div>
)
))}
</div>
</div>
{/* Flagged segments list */}
<div className={styles.flagList}>
{hasFlags ? timeline.flaggedSegments.map((seg, i) => (
<motion.div
key={i}
className={`${styles.flagItem} ${styles['flagSev_' + seg.severity]}`}
initial={{ opacity: 0, x: -12 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: i * 0.1 }}
>
<div className={`${styles.flagIcon} ${styles['flagIconSev_' + seg.severity]}`}>
<AlertTriangle size={13} />
</div>
<div className={styles.flagContent}>
<p className={styles.flagReason}>{seg.reason}</p>
<p className={styles.flagMeta}>
<Clock size={10} />
<span className="font-mono">{seg.start.toFixed(1)}s – {seg.end.toFixed(1)}s</span>
<span>·</span>
<span className="font-mono">{seg.frames.length} frames flagged</span>
</p>
</div>
<span className={`${styles.flagBadge} ${styles['badgeSev_' + seg.severity]}`}>
{SEV_LABELS[seg.severity]}
</span>
</motion.div>
)) : (
<div className={styles.emptyState}>
<CheckCircle size={16} />
<span>No suspicious temporal segments crossed the current threshold.</span>
</div>
)}
</div>
</div>
);
}