fedimint_gateway_ui/
payment_summary.rs

1use std::time::{Duration, UNIX_EPOCH};
2
3use fedimint_core::time::now;
4use fedimint_gateway_common::{PaymentStats, PaymentSummaryPayload, PaymentSummaryResponse};
5use maud::{Markup, html};
6
7use crate::DynGatewayApi;
8
9pub async fn render<E>(api: &DynGatewayApi<E>) -> Markup
10where
11    E: std::fmt::Display,
12{
13    let now = now();
14    let now_millis = now
15        .duration_since(UNIX_EPOCH)
16        .expect("Before unix epoch")
17        .as_millis() as u64;
18
19    let one_day_ago = now
20        .checked_sub(Duration::from_secs(60 * 60 * 24))
21        .expect("Before unix epoch");
22    let one_day_ago_millis = one_day_ago
23        .duration_since(UNIX_EPOCH)
24        .expect("Before unix epoch")
25        .as_millis() as u64;
26
27    // Fetch payment summary safely
28    let payment_summary = api
29        .handle_payment_summary_msg(PaymentSummaryPayload {
30            start_millis: one_day_ago_millis,
31            end_millis: now_millis,
32        })
33        .await;
34
35    match payment_summary {
36        Ok(summary) => render_summary(&summary),
37        Err(e) => html! {
38            div class="card h-100 border-danger" {
39                div class="card-header dashboard-header bg-danger text-white" {
40                    "Payment Summary"
41                }
42                div class="card-body" {
43                    div class="alert alert-danger mb-0" {
44                        strong { "Failed to fetch payment summary: " }
45                        (e.to_string())
46                    }
47                }
48            }
49        },
50    }
51}
52
53fn render_summary(summary: &PaymentSummaryResponse) -> Markup {
54    html! {
55        div class="card h-100" {
56            div class="card-header dashboard-header" { "Payment Summary (Last 24h)" }
57            div class="card-body" {
58                div class="row" {
59                    div class="col-md-6" {
60                        (render_stats_table("Outgoing Payments", &summary.outgoing, "text-danger"))
61                    }
62                    div class="col-md-6" {
63                        (render_stats_table("Incoming Payments", &summary.incoming, "text-success"))
64                    }
65                }
66            }
67        }
68    }
69}
70
71fn render_stats_table(title: &str, stats: &PaymentStats, title_class: &str) -> Markup {
72    html! {
73        div {
74            h5 class=(format!("{} mb-3", title_class)) { (title) }
75
76            table class="table table-sm mb-0" {
77                tbody {
78                    tr {
79                        th { "✅ Total Success" }
80                        td { (stats.total_success) }
81                    }
82                    tr {
83                        th { "❌ Total Failure" }
84                        td { (stats.total_failure) }
85                    }
86                    tr {
87                        th { "💸 Total Fees" }
88                        td { (format!("{} msats", stats.total_fees.msats)) }
89                    }
90                    tr {
91                        th { "⚡ Average Latency" }
92                        td {
93                            (match stats.average_latency {
94                                Some(d) => format_duration(d),
95                                None => "—".into(),
96                            })
97                        }
98                    }
99                    tr {
100                        th { "📈 Median Latency" }
101                        td {
102                            (match stats.median_latency {
103                                Some(d) => format_duration(d),
104                                None => "—".into(),
105                            })
106                        }
107                    }
108                }
109            }
110        }
111    }
112}
113
114fn format_duration(d: Duration) -> String {
115    if d.as_secs() > 0 {
116        format!("{:.2}s", d.as_secs_f64())
117    } else {
118        format!("{} ms", d.as_millis())
119    }
120}