119 lines
4.1 KiB
Rust
119 lines
4.1 KiB
Rust
use ratatui::buffer::Buffer;
|
|
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
|
use ratatui::style::{Modifier, Style};
|
|
use ratatui::text::{Line, Span};
|
|
use ratatui::widgets::{Block, Paragraph, Widget};
|
|
|
|
use crate::session::result::LessonResult;
|
|
use crate::ui::theme::Theme;
|
|
|
|
pub struct Dashboard<'a> {
|
|
pub result: &'a LessonResult,
|
|
pub theme: &'a Theme,
|
|
}
|
|
|
|
impl<'a> Dashboard<'a> {
|
|
pub fn new(result: &'a LessonResult, theme: &'a Theme) -> Self {
|
|
Self { result, theme }
|
|
}
|
|
}
|
|
|
|
impl Widget for Dashboard<'_> {
|
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
let colors = &self.theme.colors;
|
|
|
|
let block = Block::bordered()
|
|
.title(" Lesson Complete ")
|
|
.border_style(Style::default().fg(colors.accent()))
|
|
.style(Style::default().bg(colors.bg()));
|
|
let inner = block.inner(area);
|
|
block.render(area, buf);
|
|
|
|
let layout = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints([
|
|
Constraint::Length(2),
|
|
Constraint::Length(3),
|
|
Constraint::Length(3),
|
|
Constraint::Length(3),
|
|
Constraint::Length(3),
|
|
Constraint::Min(0),
|
|
Constraint::Length(2),
|
|
])
|
|
.split(inner);
|
|
|
|
let title = Paragraph::new(Line::from(Span::styled(
|
|
"Results",
|
|
Style::default()
|
|
.fg(colors.accent())
|
|
.add_modifier(Modifier::BOLD),
|
|
)))
|
|
.alignment(Alignment::Center);
|
|
title.render(layout[0], buf);
|
|
|
|
let wpm_text = format!("{:.0} WPM", self.result.wpm);
|
|
let cpm_text = format!(" ({:.0} CPM)", self.result.cpm);
|
|
let wpm_line = Line::from(vec![
|
|
Span::styled(" Speed: ", Style::default().fg(colors.fg())),
|
|
Span::styled(
|
|
&*wpm_text,
|
|
Style::default()
|
|
.fg(colors.accent())
|
|
.add_modifier(Modifier::BOLD),
|
|
),
|
|
Span::styled(&*cpm_text, Style::default().fg(colors.text_pending())),
|
|
]);
|
|
Paragraph::new(wpm_line).render(layout[1], buf);
|
|
|
|
let acc_color = if self.result.accuracy >= 95.0 {
|
|
colors.success()
|
|
} else if self.result.accuracy >= 85.0 {
|
|
colors.warning()
|
|
} else {
|
|
colors.error()
|
|
};
|
|
let acc_text = format!("{:.1}%", self.result.accuracy);
|
|
let acc_detail = format!(
|
|
" ({}/{} correct)",
|
|
self.result.correct, self.result.total_chars
|
|
);
|
|
let acc_line = Line::from(vec![
|
|
Span::styled(" Accuracy: ", Style::default().fg(colors.fg())),
|
|
Span::styled(
|
|
&*acc_text,
|
|
Style::default().fg(acc_color).add_modifier(Modifier::BOLD),
|
|
),
|
|
Span::styled(&*acc_detail, Style::default().fg(colors.text_pending())),
|
|
]);
|
|
Paragraph::new(acc_line).render(layout[2], buf);
|
|
|
|
let time_text = format!("{:.1}s", self.result.elapsed_secs);
|
|
let time_line = Line::from(vec![
|
|
Span::styled(" Time: ", Style::default().fg(colors.fg())),
|
|
Span::styled(&*time_text, Style::default().fg(colors.fg())),
|
|
]);
|
|
Paragraph::new(time_line).render(layout[3], buf);
|
|
|
|
let error_text = format!("{}", self.result.incorrect);
|
|
let chars_line = Line::from(vec![
|
|
Span::styled(" Errors: ", Style::default().fg(colors.fg())),
|
|
Span::styled(
|
|
&*error_text,
|
|
Style::default().fg(if self.result.incorrect == 0 {
|
|
colors.success()
|
|
} else {
|
|
colors.error()
|
|
}),
|
|
),
|
|
]);
|
|
Paragraph::new(chars_line).render(layout[4], buf);
|
|
|
|
let help = Paragraph::new(Line::from(vec![
|
|
Span::styled(" [r] Retry ", Style::default().fg(colors.accent())),
|
|
Span::styled("[q] Menu ", Style::default().fg(colors.accent())),
|
|
Span::styled("[s] Stats", Style::default().fg(colors.accent())),
|
|
]));
|
|
help.render(layout[6], buf);
|
|
}
|
|
}
|