Spaces:
Sleeping
Sleeping
| import React, { useEffect, useRef } from 'react'; | |
| const ACTION_CONFIG = { | |
| reply: { icon: '↩', color: '#3b82f6', label: 'Reply' }, | |
| schedule: { icon: '📅', color: '#8b5cf6', label: 'Schedule' }, | |
| delegate: { icon: '→', color: '#f97316', label: 'Delegate' }, | |
| ignore: { icon: '✕', color: '#64748b', label: 'Ignore' }, | |
| }; | |
| const PRIORITY_COLORS = { 5: '#ef4444', 4: '#f59e0b', 3: '#eab308', 2: '#22c55e', 1: '#94a3b8' }; | |
| export default function TimelinePanel({ history }) { | |
| const endRef = useRef(null); | |
| useEffect(() => { | |
| endRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'end' }); | |
| }, [history.length]); | |
| return ( | |
| <div className="panel timeline-panel"> | |
| <div className="panel-header"> | |
| <span className="panel-title">📊 Action Timeline</span> | |
| <span className="panel-badge">{history.length} actions</span> | |
| </div> | |
| <div className="timeline-scroll"> | |
| {history.length === 0 ? ( | |
| <div className="timeline-empty"> | |
| <span className="timeline-empty-icon">⏱</span> | |
| <span>No actions yet — click <strong>Watch AI Play</strong> or select an email above</span> | |
| </div> | |
| ) : ( | |
| <div className="timeline-track"> | |
| {history.map((entry, i) => { | |
| const cfg = ACTION_CONFIG[entry.action] || ACTION_CONFIG.ignore; | |
| const positive = entry.reward >= 0; | |
| const isLast = i === history.length - 1; | |
| return ( | |
| <div key={i} className={`timeline-entry ${isLast ? 'latest' : ''}`}> | |
| {/* Connector line */} | |
| {i > 0 && <div className="timeline-line" style={{ background: `${cfg.color}40` }} />} | |
| {/* Step badge */} | |
| <div className="timeline-step" style={{ background: cfg.color }}> | |
| {entry.step + 1} | |
| </div> | |
| {/* Card */} | |
| <div className="timeline-card" style={{ borderColor: `${cfg.color}40` }}> | |
| <div className="timeline-card-top"> | |
| <span className="tl-time">{entry.time}</span> | |
| <span className="tl-action" style={{ color: cfg.color }}> | |
| {cfg.icon} {cfg.label} | |
| </span> | |
| <span | |
| className="tl-reward" | |
| style={{ color: positive ? '#22c55e' : '#ef4444' }} | |
| > | |
| {positive ? '+' : ''}{entry.reward.toFixed(3)} | |
| </span> | |
| </div> | |
| <div className="timeline-card-body"> | |
| <span | |
| className="tl-dot" | |
| style={{ background: PRIORITY_COLORS[entry.email_priority] || '#94a3b8' }} | |
| /> | |
| <span className="tl-subject">{entry.email_subject}</span> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| <div ref={endRef} /> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |