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