use crossterm::event::KeyCode;
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
widgets::{Block, Borders, Cell, Paragraph, Row, Table, TableState},
Frame,
};
use crate::history::HistoryEntry;
pub struct HistoryPanel {
pub entries: Vec<HistoryEntry>,
pub state: TableState,
}
impl HistoryPanel {
pub fn new(entries: Vec<HistoryEntry>) -> Self {
let mut state = TableState::default();
if !entries.is_empty() {
state.select(Some(0));
}
Self { entries, state }
}
pub fn handle_key(&mut self, code: KeyCode) {
match code {
KeyCode::Char('j') | KeyCode::Down => self.next(1),
KeyCode::Char('k') | KeyCode::Up => self.prev(1),
KeyCode::PageDown => self.next(10),
KeyCode::PageUp => self.prev(10),
_ => {}
}
}
fn next(&mut self, step: usize) {
let len = self.entries.len();
if len == 0 { return; }
let i = self.state.selected().map_or(0, |i| (i + step).min(len - 1));
self.state.select(Some(i));
}
fn prev(&mut self, step: usize) {
let i = self.state.selected().map_or(0, |i| i.saturating_sub(step));
self.state.select(Some(i));
}
pub fn render(&self, f: &mut Frame, area: Rect) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(3)])
.split(area);
let rows: Vec<Row> = self
.entries
.iter()
.map(|e| {
let timestamp = e.timestamp.format("%Y-%m-%d %H:%M").to_string();
let pkg_summary: String = e
.packages
.iter()
.take(3)
.map(|p| {
if let Some(ref nv) = p.new_version {
format!("{} → {}", p.name, nv)
} else {
p.name.clone()
}
})
.collect::<Vec<_>>()
.join(", ");
let summary = if e.packages.len() > 3 {
format!("{} (+{})", pkg_summary, e.packages.len() - 3)
} else {
pkg_summary
};
Row::new(vec![
Cell::from(format!("#{}", e.id)),
Cell::from(timestamp),
Cell::from(e.operation.clone()),
Cell::from(summary),
])
})
.collect();
let widths = [
Constraint::Length(5),
Constraint::Length(18),
Constraint::Length(12),
Constraint::Min(30),
];
let table = Table::new(rows, widths)
.header(
Row::new(vec!["ID", "Time", "Operation", "Packages"])
.style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
)
.block(Block::default().borders(Borders::ALL).title(" Transaction History "))
.row_highlight_style(Style::default().bg(Color::DarkGray).add_modifier(Modifier::BOLD))
.highlight_symbol("▶ ");
let mut state = self.state.clone();
f.render_stateful_widget(table, chunks[0], &mut state);
let actions = Paragraph::new(" Enter: expand details u: undo transaction q: back [?] Help")
.block(Block::default().borders(Borders::ALL).title(" Actions "));
f.render_widget(actions, chunks[1]);
}
}