Skip to main content

batuta/stack/diagnostics/
dashboard.rs

1//! Dashboard rendering for stack diagnostics
2//!
3//! This module provides ASCII dashboard rendering functionality for
4//! visualizing stack health status in a terminal-friendly format.
5
6use super::engine::StackDiagnostics;
7
8// ============================================================================
9// Dashboard Renderer
10// ============================================================================
11
12/// Render diagnostics as ASCII dashboard
13pub fn render_dashboard(diagnostics: &StackDiagnostics) -> String {
14    let mut output = String::new();
15    let summary = diagnostics.health_summary();
16
17    // Header
18    output
19        .push_str("┌─────────────────────────────────────────────────────────────────────────┐\n");
20    output
21        .push_str("│                  SOVEREIGN AI STACK HEALTH DASHBOARD                    │\n");
22    output.push_str(&format!(
23        "│                  Timestamp: {:40} │\n",
24        chrono::Utc::now().format("%Y-%m-%d %H:%M:%S")
25    ));
26    output
27        .push_str("├─────────────────────────────────────────────────────────────────────────┤\n");
28
29    // Andon Status
30    output
31        .push_str("│                                                                         │\n");
32    output.push_str(&format!("│  ANDON STATUS: {} {:55}│\n", summary.andon_status, ""));
33    output
34        .push_str("│                                                                         │\n");
35
36    // Stack Summary
37    output.push_str("│  ═══════════════════════════════════════════════════════════════════   │\n");
38    output
39        .push_str("│  STACK SUMMARY                                                          │\n");
40    output.push_str("│  ═══════════════════════════════════════════════════════════════════   │\n");
41    output
42        .push_str("│                                                                         │\n");
43    output.push_str(&format!(
44        "│  Total Components:    {:3}                                              │\n",
45        summary.total_components
46    ));
47    output.push_str(&format!(
48        "│  Healthy:             {:3} ({:.0}%)                                         │\n",
49        summary.green_count,
50        summary.health_percentage()
51    ));
52    output.push_str(&format!(
53        "│  Warnings:            {:3} ({:.0}%)                                         │\n",
54        summary.yellow_count,
55        if summary.total_components > 0 {
56            (summary.yellow_count as f64 / summary.total_components as f64) * 100.0
57        } else {
58            0.0
59        }
60    ));
61    output.push_str(&format!(
62        "│  Critical:            {:3} ({:.0}%)                                         │\n",
63        summary.red_count,
64        if summary.total_components > 0 {
65            (summary.red_count as f64 / summary.total_components as f64) * 100.0
66        } else {
67            0.0
68        }
69    ));
70    output.push_str(&format!(
71        "│  Average Demo Score:  {:.1}/100                                          │\n",
72        summary.avg_demo_score
73    ));
74    output.push_str(&format!(
75        "│  Average Coverage:    {:.1}%                                             │\n",
76        summary.avg_coverage
77    ));
78    output
79        .push_str("│                                                                         │\n");
80
81    // Anomalies
82    let anomalies = diagnostics.anomalies();
83    if !anomalies.is_empty() {
84        output.push_str(
85            "│  ═══════════════════════════════════════════════════════════════════   │\n",
86        );
87        output.push_str(
88            "│  ANOMALIES DETECTED                                                     │\n",
89        );
90        output.push_str(
91            "│  ═══════════════════════════════════════════════════════════════════   │\n",
92        );
93        output.push_str(
94            "│                                                                         │\n",
95        );
96
97        for anomaly in anomalies.iter().take(5) {
98            let icon = if anomaly.is_critical() { "🔴" } else { "⚠️" };
99            output.push_str(&format!(
100                "│  {}  {}: {}                               │\n",
101                icon, anomaly.component, anomaly.description
102            ));
103        }
104        output.push_str(
105            "│                                                                         │\n",
106        );
107    }
108
109    output
110        .push_str("└─────────────────────────────────────────────────────────────────────────┘\n");
111
112    output
113}