ringkernel_procint/gui/panels/
kpi_panel.rs1use crate::analytics::{HealthStatus, ProcessKPIs};
4use crate::gui::widgets::{conformance_gauge, kpi_card, sparkline};
5use crate::gui::{section_header, styled_panel, Theme};
6use eframe::egui::Ui;
7use std::collections::VecDeque;
8
9pub struct KpiPanel {
11 throughput_history: VecDeque<f32>,
13 fitness_history: VecDeque<f32>,
15 max_history: usize,
17}
18
19impl Default for KpiPanel {
20 fn default() -> Self {
21 Self {
22 throughput_history: VecDeque::with_capacity(50),
23 fitness_history: VecDeque::with_capacity(50),
24 max_history: 50,
25 }
26 }
27}
28
29impl KpiPanel {
30 pub fn update(&mut self, kpis: &ProcessKPIs) {
32 self.throughput_history
33 .push_back(kpis.events_per_second as f32);
34 if self.throughput_history.len() > self.max_history {
35 self.throughput_history.pop_front();
36 }
37
38 self.fitness_history.push_back(kpis.avg_fitness);
39 if self.fitness_history.len() > self.max_history {
40 self.fitness_history.pop_front();
41 }
42 }
43
44 pub fn render(&mut self, ui: &mut Ui, theme: &Theme, kpis: &ProcessKPIs) {
46 styled_panel(ui, theme, |ui| {
47 section_header(ui, theme, "KEY METRICS");
48
49 ui.horizontal(|ui| {
51 kpi_card(ui, theme, "Throughput", &kpis.format_throughput(), None);
52 });
53
54 let history: Vec<f32> = self.throughput_history.iter().copied().collect();
56 if !history.is_empty() {
57 sparkline(ui, theme, &history, ui.available_width(), 30.0);
58 }
59
60 ui.add_space(12.0);
61
62 ui.horizontal(|ui| {
64 conformance_gauge(ui, theme, kpis.avg_fitness, 80.0);
65 ui.vertical(|ui| {
66 ui.label(
67 egui::RichText::new("Fitness")
68 .size(11.0)
69 .color(theme.text_muted),
70 );
71 ui.label(
72 egui::RichText::new(kpis.format_fitness())
73 .size(16.0)
74 .strong(),
75 );
76
77 let health = kpis.health_status();
78 let health_color = match health {
79 HealthStatus::Excellent => theme.success,
80 HealthStatus::Good => theme.accent,
81 HealthStatus::Warning => theme.warning,
82 HealthStatus::Critical => theme.error,
83 };
84 ui.label(
85 egui::RichText::new(health.name())
86 .size(11.0)
87 .color(health_color),
88 );
89 });
90 });
91
92 ui.add_space(12.0);
93
94 kpi_card(ui, theme, "Avg Duration", &kpis.format_duration(), None);
96
97 ui.add_space(8.0);
98
99 ui.horizontal(|ui| {
101 ui.vertical(|ui| {
102 ui.label(
103 egui::RichText::new("Cases")
104 .size(11.0)
105 .color(theme.text_muted),
106 );
107 ui.label(
108 egui::RichText::new(format!("{}", kpis.cases_completed))
109 .size(14.0)
110 .strong(),
111 );
112 });
113 ui.add_space(20.0);
114 ui.vertical(|ui| {
115 ui.label(
116 egui::RichText::new("Patterns")
117 .size(11.0)
118 .color(theme.text_muted),
119 );
120 ui.label(
121 egui::RichText::new(format!("{}", kpis.pattern_count))
122 .size(14.0)
123 .strong(),
124 );
125 });
126 });
127 });
128 }
129}