1use super::super::chart::ChartDataset;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
17pub struct MetricsCardsData {
18 pub cards: Vec<MetricCard>,
19}
20
21impl MetricsCardsData {
22 pub const fn new(cards: Vec<MetricCard>) -> Self {
23 Self { cards }
24 }
25
26 pub fn add_card(mut self, card: MetricCard) -> Self {
27 self.cards.push(card);
28 self
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
33pub struct MetricCard {
34 pub title: String,
35 pub value: String,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub subtitle: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub icon: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub status: Option<MetricStatus>,
42}
43
44#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
45#[serde(rename_all = "lowercase")]
46pub enum MetricStatus {
47 Success,
48 Warning,
49 Error,
50 #[default]
51 Info,
52}
53
54impl std::str::FromStr for MetricStatus {
55 type Err = String;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 match s.to_lowercase().as_str() {
59 "success" | "healthy" | "ok" | "active" => Ok(Self::Success),
60 "warning" | "degraded" => Ok(Self::Warning),
61 "error" | "failed" | "critical" => Ok(Self::Error),
62 "info" | "unknown" => Ok(Self::Info),
63 _ => Err(format!("Invalid metric status: {s}")),
64 }
65 }
66}
67
68impl MetricCard {
69 pub fn new(title: impl Into<String>, value: impl Into<String>) -> Self {
70 Self {
71 title: title.into(),
72 value: value.into(),
73 subtitle: None,
74 icon: None,
75 status: None,
76 }
77 }
78
79 pub fn with_subtitle(mut self, subtitle: impl Into<String>) -> Self {
80 self.subtitle = Some(subtitle.into());
81 self
82 }
83
84 pub fn with_icon(mut self, icon: impl Into<String>) -> Self {
85 self.icon = Some(icon.into());
86 self
87 }
88
89 pub const fn with_status(mut self, status: MetricStatus) -> Self {
90 self.status = Some(status);
91 self
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
96pub struct ChartSectionData {
97 pub chart_type: String,
98 pub labels: Vec<String>,
99 pub datasets: Vec<ChartDataset>,
100}
101
102impl ChartSectionData {
103 pub fn new(
104 chart_type: impl Into<String>,
105 labels: Vec<String>,
106 datasets: Vec<ChartDataset>,
107 ) -> Self {
108 Self {
109 chart_type: chart_type.into(),
110 labels,
111 datasets,
112 }
113 }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
117pub struct TableSectionData {
118 pub columns: Vec<String>,
119 pub rows: Vec<serde_json::Value>,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub sortable: Option<bool>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub default_sort: Option<SortConfig>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
127pub struct SortConfig {
128 pub column: String,
129 pub order: String,
130}
131
132impl TableSectionData {
133 pub const fn new(columns: Vec<String>, rows: Vec<serde_json::Value>) -> Self {
134 Self {
135 columns,
136 rows,
137 sortable: None,
138 default_sort: None,
139 }
140 }
141
142 pub const fn with_sortable(mut self, sortable: bool) -> Self {
143 self.sortable = Some(sortable);
144 self
145 }
146
147 pub fn with_default_sort(
148 mut self,
149 column: impl Into<String>,
150 order: impl Into<String>,
151 ) -> Self {
152 self.default_sort = Some(SortConfig {
153 column: column.into(),
154 order: order.into(),
155 });
156 self
157 }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
161pub struct StatusSectionData {
162 pub services: Vec<ServiceStatus>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub database: Option<DatabaseStatus>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub recent_errors: Option<ErrorCounts>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
170pub struct ServiceStatus {
171 pub name: String,
172 pub status: String,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub uptime: Option<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
178pub struct DatabaseStatus {
179 pub size_mb: f64,
180 pub status: String,
181}
182
183#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
184pub struct ErrorCounts {
185 pub critical: i32,
186 pub error: i32,
187 pub warn: i32,
188}
189
190impl StatusSectionData {
191 pub const fn new(services: Vec<ServiceStatus>) -> Self {
192 Self {
193 services,
194 database: None,
195 recent_errors: None,
196 }
197 }
198
199 pub fn with_database(mut self, status: DatabaseStatus) -> Self {
200 self.database = Some(status);
201 self
202 }
203
204 pub const fn with_error_counts(mut self, counts: ErrorCounts) -> Self {
205 self.recent_errors = Some(counts);
206 self
207 }
208}
209
210impl ServiceStatus {
211 pub fn new(name: impl Into<String>, status: impl Into<String>) -> Self {
212 Self {
213 name: name.into(),
214 status: status.into(),
215 uptime: None,
216 }
217 }
218
219 pub fn with_uptime(mut self, uptime: impl Into<String>) -> Self {
220 self.uptime = Some(uptime.into());
221 self
222 }
223}
224
225impl DatabaseStatus {
226 pub fn new(size_mb: f64, status: impl Into<String>) -> Self {
227 Self {
228 size_mb,
229 status: status.into(),
230 }
231 }
232}
233
234impl ErrorCounts {
235 pub const fn new(critical: i32, error: i32, warn: i32) -> Self {
236 Self {
237 critical,
238 error,
239 warn,
240 }
241 }
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
245pub struct ListSectionData {
246 pub lists: Vec<ItemList>,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
250pub struct ItemList {
251 pub title: String,
252 pub items: Vec<ListItem>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
256pub struct ListItem {
257 pub rank: i32,
258 pub label: String,
259 pub value: String,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 pub badge: Option<String>,
262}
263
264impl ListSectionData {
265 pub const fn new(lists: Vec<ItemList>) -> Self {
266 Self { lists }
267 }
268}
269
270impl ItemList {
271 pub fn new(title: impl Into<String>, items: Vec<ListItem>) -> Self {
272 Self {
273 title: title.into(),
274 items,
275 }
276 }
277}
278
279impl ListItem {
280 pub fn new(rank: i32, label: impl Into<String>, value: impl Into<String>) -> Self {
281 Self {
282 rank,
283 label: label.into(),
284 value: value.into(),
285 badge: None,
286 }
287 }
288
289 pub fn with_badge(mut self, badge: impl Into<String>) -> Self {
290 self.badge = Some(badge.into());
291 self
292 }
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
296pub struct TextSectionData {
297 pub text: String,
298}
299
300impl TextSectionData {
301 pub fn new(text: impl Into<String>) -> Self {
302 Self { text: text.into() }
303 }
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
307pub struct TimelineSectionData {
308 pub events: Vec<TimelineEvent>,
309}
310
311impl TimelineSectionData {
312 pub const fn new(events: Vec<TimelineEvent>) -> Self {
313 Self { events }
314 }
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
318pub struct TimelineEvent {
319 pub timestamp: String,
320 pub label: String,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub description: Option<String>,
323}
324
325impl TimelineEvent {
326 pub fn new(timestamp: impl Into<String>, label: impl Into<String>) -> Self {
327 Self {
328 timestamp: timestamp.into(),
329 label: label.into(),
330 description: None,
331 }
332 }
333
334 pub fn with_description(mut self, description: impl Into<String>) -> Self {
335 self.description = Some(description.into());
336 self
337 }
338}