Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I20f205d9e06a93a89e8f4433ed6f80576a6a6964
181 lines
5.4 KiB
Rust
181 lines
5.4 KiB
Rust
use ratatui::{
|
|
Frame,
|
|
layout::{Constraint, Direction, Layout, Rect},
|
|
style::{Color, Modifier, Style},
|
|
text::{Line, Span},
|
|
widgets::{Block, Borders, Paragraph, Row, Table},
|
|
};
|
|
|
|
use crate::app::AppState;
|
|
|
|
pub fn render(f: &mut Frame, state: &AppState, area: Rect) {
|
|
let Some(ref stats) = state.library_stats else {
|
|
let msg = Paragraph::new("Loading statistics... (press X to refresh)")
|
|
.block(Block::default().borders(Borders::ALL).title(" Statistics "));
|
|
f.render_widget(msg, area);
|
|
return;
|
|
};
|
|
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints([
|
|
Constraint::Length(8), // Overview
|
|
Constraint::Length(10), // Media by type
|
|
Constraint::Min(6), // Top tags & collections
|
|
])
|
|
.split(area);
|
|
|
|
// Overview section
|
|
let overview_lines = vec![
|
|
Line::from(vec![
|
|
Span::styled(" Total Media: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.total_media.to_string(),
|
|
Style::default()
|
|
.fg(Color::Cyan)
|
|
.add_modifier(Modifier::BOLD),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled("Total Size: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
super::format_size(stats.total_size_bytes),
|
|
Style::default()
|
|
.fg(Color::Cyan)
|
|
.add_modifier(Modifier::BOLD),
|
|
),
|
|
]),
|
|
Line::from(vec![
|
|
Span::styled(" Avg Size: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
super::format_size(stats.avg_file_size_bytes),
|
|
Style::default().fg(Color::White),
|
|
),
|
|
]),
|
|
Line::from(vec![
|
|
Span::styled(" Tags: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.total_tags.to_string(),
|
|
Style::default().fg(Color::Green),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled("Collections: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.total_collections.to_string(),
|
|
Style::default().fg(Color::Green),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled("Duplicates: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.total_duplicates.to_string(),
|
|
Style::default().fg(Color::Yellow),
|
|
),
|
|
]),
|
|
Line::from(vec![
|
|
Span::styled(" Newest: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.newest_item.as_deref().map_or("-", super::format_date),
|
|
Style::default().fg(Color::White),
|
|
),
|
|
Span::raw(" "),
|
|
Span::styled("Oldest: ", Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
stats.oldest_item.as_deref().map_or("-", super::format_date),
|
|
Style::default().fg(Color::White),
|
|
),
|
|
]),
|
|
];
|
|
|
|
let overview = Paragraph::new(overview_lines)
|
|
.block(Block::default().borders(Borders::ALL).title(" Overview "));
|
|
f.render_widget(overview, chunks[0]);
|
|
|
|
// Media by Type table
|
|
let type_rows: Vec<Row> = stats
|
|
.media_by_type
|
|
.iter()
|
|
.map(|tc| {
|
|
let color = super::media_type_color(&tc.name);
|
|
Row::new(vec![
|
|
Span::styled(tc.name.clone(), Style::default().fg(color)),
|
|
Span::styled(tc.count.to_string(), Style::default().fg(Color::White)),
|
|
])
|
|
})
|
|
.collect();
|
|
|
|
let storage_rows: Vec<Row> = stats
|
|
.storage_by_type
|
|
.iter()
|
|
.map(|tc| {
|
|
Row::new(vec![
|
|
Span::styled(tc.name.clone(), Style::default().fg(Color::Gray)),
|
|
Span::styled(
|
|
super::format_size(tc.count),
|
|
Style::default().fg(Color::White),
|
|
),
|
|
])
|
|
})
|
|
.collect();
|
|
|
|
let type_cols = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
|
.split(chunks[1]);
|
|
|
|
let type_table =
|
|
Table::new(type_rows, [Constraint::Min(20), Constraint::Length(10)]).block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(" Media by Type "),
|
|
);
|
|
f.render_widget(type_table, type_cols[0]);
|
|
|
|
let storage_table =
|
|
Table::new(storage_rows, [Constraint::Min(20), Constraint::Length(12)])
|
|
.block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(" Storage by Type "),
|
|
);
|
|
f.render_widget(storage_table, type_cols[1]);
|
|
|
|
// Top tags and collections
|
|
let bottom_cols = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
|
.split(chunks[2]);
|
|
|
|
let tag_rows: Vec<Row> = stats
|
|
.top_tags
|
|
.iter()
|
|
.map(|tc| {
|
|
Row::new(vec![
|
|
Span::styled(tc.name.clone(), Style::default().fg(Color::Green)),
|
|
Span::styled(tc.count.to_string(), Style::default().fg(Color::White)),
|
|
])
|
|
})
|
|
.collect();
|
|
|
|
let tags_table =
|
|
Table::new(tag_rows, [Constraint::Min(20), Constraint::Length(10)])
|
|
.block(Block::default().borders(Borders::ALL).title(" Top Tags "));
|
|
f.render_widget(tags_table, bottom_cols[0]);
|
|
|
|
let col_rows: Vec<Row> = stats
|
|
.top_collections
|
|
.iter()
|
|
.map(|tc| {
|
|
Row::new(vec![
|
|
Span::styled(tc.name.clone(), Style::default().fg(Color::Magenta)),
|
|
Span::styled(tc.count.to_string(), Style::default().fg(Color::White)),
|
|
])
|
|
})
|
|
.collect();
|
|
|
|
let cols_table =
|
|
Table::new(col_rows, [Constraint::Min(20), Constraint::Length(10)]).block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.title(" Top Collections "),
|
|
);
|
|
f.render_widget(cols_table, bottom_cols[1]);
|
|
}
|