feat: added langflow, langfuse and langgraph integrations (#17)
Some checks failed
Some checks failed
Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com> Reviewed-on: #17
This commit was merged in pull request #17.
This commit is contained in:
@@ -1,40 +1,142 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_free_icons::icons::bs_icons::{
|
||||
BsBarChartLine, BsBoxArrowUpRight, BsGraphUp, BsSpeedometer,
|
||||
};
|
||||
use dioxus_free_icons::Icon;
|
||||
|
||||
use crate::i18n::{t, Locale};
|
||||
use crate::models::AnalyticsMetric;
|
||||
use crate::models::{AnalyticsMetric, ServiceUrlsContext};
|
||||
|
||||
/// Analytics page placeholder for LangFuse integration.
|
||||
/// Analytics & Observability page for Langfuse.
|
||||
///
|
||||
/// Shows a "Coming Soon" card with a disabled launch button,
|
||||
/// plus a mock stats bar showing sample metrics.
|
||||
/// Langfuse is configured with Keycloak SSO (shared realm with CERTifAI).
|
||||
/// When users open Langfuse, the existing Keycloak session auto-authenticates
|
||||
/// them transparently. This page shows a metrics bar, connection status,
|
||||
/// and a prominent button to open Langfuse in a new tab.
|
||||
#[component]
|
||||
pub fn AnalyticsPage() -> Element {
|
||||
let locale = use_context::<Signal<Locale>>();
|
||||
let svc = use_context::<Signal<ServiceUrlsContext>>();
|
||||
let l = *locale.read();
|
||||
let url = svc.read().langfuse_url.clone();
|
||||
|
||||
let connected = !url.is_empty();
|
||||
let metrics = mock_metrics(l);
|
||||
|
||||
rsx! {
|
||||
section { class: "placeholder-page",
|
||||
div { class: "analytics-page",
|
||||
// -- Hero section --
|
||||
div { class: "analytics-hero",
|
||||
div { class: "analytics-hero-row",
|
||||
div { class: "analytics-hero-icon",
|
||||
Icon { icon: BsGraphUp, width: 24, height: 24 }
|
||||
}
|
||||
h2 { class: "analytics-hero-title",
|
||||
{t(l, "developer.analytics_title")}
|
||||
}
|
||||
}
|
||||
p { class: "analytics-hero-desc",
|
||||
{t(l, "developer.analytics_desc")}
|
||||
}
|
||||
|
||||
// -- Connection status --
|
||||
if connected {
|
||||
div { class: "agents-status",
|
||||
span {
|
||||
class: "agents-status-dot agents-status-dot--on",
|
||||
}
|
||||
span { {t(l, "developer.analytics_status_connected")} }
|
||||
code { class: "agents-status-url", {url.clone()} }
|
||||
}
|
||||
} else {
|
||||
div { class: "agents-status",
|
||||
span {
|
||||
class: "agents-status-dot agents-status-dot--off",
|
||||
}
|
||||
span { {t(l, "developer.analytics_status_not_connected")} }
|
||||
span { class: "agents-status-hint",
|
||||
{t(l, "developer.analytics_config_hint")}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- SSO info --
|
||||
if connected {
|
||||
p { class: "analytics-sso-hint",
|
||||
{t(l, "developer.analytics_sso_hint")}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Metrics bar --
|
||||
div { class: "analytics-stats-bar",
|
||||
for metric in &metrics {
|
||||
div { class: "analytics-stat",
|
||||
span { class: "analytics-stat-value", "{metric.value}" }
|
||||
span { class: "analytics-stat-label", "{metric.label}" }
|
||||
span { class: if metric.change_pct >= 0.0 { "analytics-stat-change analytics-stat-change--up" } else { "analytics-stat-change analytics-stat-change--down" },
|
||||
span {
|
||||
class: if metric.change_pct >= 0.0 {
|
||||
"analytics-stat-change analytics-stat-change--up"
|
||||
} else {
|
||||
"analytics-stat-change analytics-stat-change--down"
|
||||
},
|
||||
"{metric.change_pct:+.1}%"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div { class: "placeholder-card",
|
||||
div { class: "placeholder-icon", "L" }
|
||||
h2 { "{t(l, \"developer.analytics_title\")}" }
|
||||
p { class: "placeholder-desc",
|
||||
"{t(l, \"developer.analytics_desc\")}"
|
||||
|
||||
// -- Open Langfuse button --
|
||||
if connected {
|
||||
a {
|
||||
class: "analytics-launch-btn",
|
||||
href: "{url}",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
Icon { icon: BsBoxArrowUpRight, width: 16, height: 16 }
|
||||
span { {t(l, "developer.launch_analytics")} }
|
||||
}
|
||||
}
|
||||
|
||||
// -- Quick actions --
|
||||
h3 { class: "agents-section-title",
|
||||
{t(l, "developer.analytics_quick_actions")}
|
||||
}
|
||||
|
||||
div { class: "agents-grid",
|
||||
// Traces
|
||||
a {
|
||||
class: if connected { "agents-card" } else { "agents-card agents-card--disabled" },
|
||||
href: if connected { format!("{url}/project") } else { "#".to_string() },
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
div { class: "agents-card-icon",
|
||||
Icon { icon: BsBarChartLine, width: 18, height: 18 }
|
||||
}
|
||||
div { class: "agents-card-title",
|
||||
{t(l, "developer.analytics_traces")}
|
||||
}
|
||||
div { class: "agents-card-desc",
|
||||
{t(l, "developer.analytics_traces_desc")}
|
||||
}
|
||||
}
|
||||
|
||||
// Dashboard
|
||||
a {
|
||||
class: if connected { "agents-card" } else { "agents-card agents-card--disabled" },
|
||||
href: if connected { format!("{url}/project") } else { "#".to_string() },
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
div { class: "agents-card-icon",
|
||||
Icon { icon: BsSpeedometer, width: 18, height: 18 }
|
||||
}
|
||||
div { class: "agents-card-title",
|
||||
{t(l, "developer.analytics_dashboard")}
|
||||
}
|
||||
div { class: "agents-card-desc",
|
||||
{t(l, "developer.analytics_dashboard_desc")}
|
||||
}
|
||||
}
|
||||
button { class: "btn-primary", disabled: true, "{t(l, \"developer.launch_analytics\")}" }
|
||||
span { class: "placeholder-badge", "{t(l, \"common.coming_soon\")}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user