//! Home screen — project list with stats overview. use ratatui::Frame; use ratatui::layout::{Constraint, Layout}; use ratatui::style::{Color, Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Paragraph, Row}; use crate::format; use super::App; use super::widgets; pub fn render(frame: &mut Frame, app: &App) { let area = frame.area(); let tier_label = app .user .creator_tier .as_deref() .map(format::format_tier) .unwrap_or("No tier"); let title = Line::from(vec![ Span::styled(" Makenot.work ", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" ── "), Span::styled( &app.user.username, Style::default().add_modifier(Modifier::BOLD), ), Span::raw(" ── "), Span::raw(tier_label), Span::raw(" "), ]); let block = Block::default() .title(title) .borders(Borders::ALL) .border_style(Style::default().fg(Color::Gray)); let inner = block.inner(area); frame.render_widget(block, area); let chunks = Layout::vertical([ Constraint::Length(1), // spacer Constraint::Length(3), // stats bar Constraint::Length(1), // spacer Constraint::Length(1), // section header Constraint::Min(3), // project list Constraint::Length(1), // keybindings ]) .split(inner); // Stats bar render_stats(frame, app, chunks[1]); // Section header let header = Paragraph::new(Line::from(vec![ Span::raw(" "), Span::styled("Projects", Style::default().add_modifier(Modifier::BOLD)), if app.projects.is_empty() { Span::raw("") } else { Span::raw(format!(" ({})", app.projects.len())) }, ])); frame.render_widget(header, chunks[3]); // Project list if app.loading { let loading = Paragraph::new(" Loading..."); frame.render_widget(loading, chunks[4]); } else if app.projects.is_empty() { let empty = Paragraph::new(" No projects yet."); frame.render_widget(empty, chunks[4]); } else { render_project_table(frame, app, chunks[4]); } // Keybindings let keys = Paragraph::new(Line::from(vec![ Span::raw(" "), Span::styled("[j/k]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Navigate "), Span::styled("[Enter]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Open "), Span::styled("[u]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Upload "), Span::styled("[a]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Analytics "), Span::styled("[c]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Promo "), Span::styled("[s]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Settings "), Span::styled("[r]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Refresh "), Span::styled("[q]", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" Quit"), ])) .style(Style::default().fg(Color::DarkGray)); frame.render_widget(keys, chunks[5]); } fn render_stats(frame: &mut Frame, app: &App, area: ratatui::layout::Rect) { let stats_chunks = Layout::horizontal([ Constraint::Ratio(1, 4), Constraint::Ratio(1, 4), Constraint::Ratio(1, 4), Constraint::Ratio(1, 4), ]) .split(area); let (revenue, sales, followers, items) = if let Some(ref s) = app.stats { ( format::format_cents(s.current_revenue_cents), s.current_sales.to_string(), s.current_followers.to_string(), s.total_items.to_string(), ) } else { ("--".into(), "--".into(), "--".into(), "--".into()) }; let stat_items = [ ("Revenue", &revenue), ("Sales", &sales), ("Followers", &followers), ("Items", &items), ]; for (i, (label, value)) in stat_items.iter().enumerate() { let block = Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(Color::DarkGray)); let inner = block.inner(stats_chunks[i]); frame.render_widget(block, stats_chunks[i]); let text = Paragraph::new(Line::from(vec![ Span::styled( format!(" {value}"), Style::default().add_modifier(Modifier::BOLD), ), Span::styled(format!(" {label}"), Style::default().fg(Color::DarkGray)), ])); frame.render_widget(text, inner); } } fn render_project_table(frame: &mut Frame, app: &App, area: ratatui::layout::Rect) { let rows: Vec = app .projects .iter() .enumerate() .map(|(i, p)| { let visibility = if p.is_public { "public" } else { "draft" }; Row::new(vec![ format!(" {}", p.title), format::format_project_type(&p.project_type).to_string(), visibility.to_string(), p.item_count.to_string(), format::format_cents(p.revenue_cents), ]) .style(widgets::selected_style(i, Some(app.selected_index))) }) .collect(); let widths = [ Constraint::Min(20), Constraint::Length(12), Constraint::Length(8), Constraint::Length(7), Constraint::Length(12), ]; widgets::render_table(frame, area, &[" Title", "Type", "Status", "Items", "Revenue"], &widths, rows); }