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