1use chrono::{DateTime, Local};
2use ratatui::{
3 style::{Color, Style},
4 widgets::{Block, BorderType, Borders},
5};
6
7use crate::ui::formatter::Formatter;
8
9
10pub struct StatusWidget;
11pub struct ProgressWidget;
12pub struct SummaryWidget;
13
14pub struct ColorScheme;
16
17impl ColorScheme {
18 pub const NEON_CYAN: Color = Color::Rgb(0, 255, 255);
20 pub const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
21 pub const NEON_PINK: Color = Color::Rgb(255, 16, 240);
22 pub const NEON_PURPLE: Color = Color::Rgb(188, 19, 254);
23 pub const NEON_YELLOW: Color = Color::Rgb(255, 240, 31);
24 pub const DARK_BG: Color = Color::Rgb(10, 10, 15);
25 pub const GRAY_TEXT: Color = Color::Rgb(160, 160, 160);
26 pub const WHITE_TEXT: Color = Color::Rgb(240, 240, 240);
27
28 pub fn get_context_color(context: &str) -> Color {
29 match context {
30 "terminal" => Self::NEON_CYAN,
31 "ide" => Self::NEON_PURPLE,
32 "linked" => Self::NEON_YELLOW,
33 "manual" => Color::Blue,
34 _ => Self::WHITE_TEXT,
35 }
36 }
37
38 pub fn active_status() -> Color {
39 Self::NEON_GREEN
40 }
41 pub fn project_name() -> Color {
42 Self::NEON_YELLOW
43 }
44 pub fn duration() -> Color {
45 Self::NEON_CYAN
46 }
47 pub fn path() -> Color {
48 Self::GRAY_TEXT
49 }
50 pub fn timestamp() -> Color {
51 Self::GRAY_TEXT
52 }
53 pub fn border() -> Color {
54 Self::NEON_PURPLE
55 }
56 pub fn title() -> Color {
57 Self::NEON_PINK
58 }
59
60 pub fn base_block() -> Block<'static> {
61 Block::default()
62 .borders(Borders::ALL)
63 .border_style(Style::default().fg(Self::border()))
64 .border_type(BorderType::Rounded)
65 .style(Style::default().bg(Self::DARK_BG))
66 }
67}
68
69pub struct Spinner {
70 frames: Vec<&'static str>,
71 current: usize,
72}
73
74impl Spinner {
75 pub fn new() -> Self {
76 Self {
77 frames: vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
78 current: 0,
79 }
80 }
81
82 pub fn next(&mut self) -> &'static str {
83 let frame = self.frames[self.current];
84 self.current = (self.current + 1) % self.frames.len();
85 frame
86 }
87
88 pub fn current(&self) -> &'static str {
89 self.frames[self.current]
90 }
91}
92
93impl StatusWidget {
94 pub fn render_status_text(
95 project_name: &str,
96 duration: i64,
97 start_time: &str,
98 context: &str,
99 ) -> String {
100 format!(
101 "● ACTIVE | {} | Time: {} | Started: {} | Context: {}",
102 project_name,
103 Formatter::format_duration(duration),
104 start_time,
105 context
106 )
107 }
108
109 pub fn render_idle_text() -> String {
110 "○ IDLE | No active time tracking session | Use 'tempo session start' to begin tracking"
111 .to_string()
112 }
113}
114
115impl ProgressWidget {
116 pub fn calculate_daily_progress(completed_seconds: i64, target_hours: f64) -> u16 {
117 let total_hours = completed_seconds as f64 / 3600.0;
118 let progress = (total_hours / target_hours * 100.0).min(100.0) as u16;
119 progress
120 }
121
122 pub fn format_progress_label(completed_seconds: i64, target_hours: f64) -> String {
123 let total_hours = completed_seconds as f64 / 3600.0;
124 let progress = (total_hours / target_hours * 100.0).min(100.0) as u16;
125 format!(
126 "Daily Progress ({:.1}h / {:.1}h) - {}%",
127 total_hours, target_hours, progress
128 )
129 }
130}
131
132impl SummaryWidget {
133 pub fn format_project_summary(
134 project_name: &str,
135 total_time: i64,
136 session_count: usize,
137 active_count: usize,
138 ) -> String {
139 format!(
140 "Project: {} | Total Time: {} | Sessions: {} total, {} active",
141 project_name,
142 Formatter::format_duration(total_time),
143 session_count,
144 active_count
145 )
146 }
147
148 pub fn format_session_line(
149 start_time: &DateTime<Local>,
150 duration: i64,
151 context: &str,
152 is_active: bool,
153 ) -> String {
154 let status_char = if is_active { "●" } else { "✓" };
155 let duration_str = if is_active {
156 format!("{} (active)", Formatter::format_duration(duration))
157 } else {
158 Formatter::format_duration(duration)
159 };
160
161 format!(
162 "{} {} | {} | {}",
163 status_char,
164 start_time.format("%H:%M:%S"),
165 duration_str,
166 context
167 )
168 }
169}