systemprompt_models/artifacts/dashboard/
section_data.rs

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