provisioning-platform/crates/control-center-ui/src/pages/dashboard.rs

212 lines
9.3 KiB
Rust
Raw Normal View History

use gloo_storage::{LocalStorage, Storage};
use leptos::prelude::*;
use leptos::task::spawn_local;
use crate::api::client;
use crate::components::onboarding::{NextSteps, SystemStatus, WelcomeWizard};
use crate::config::use_config;
#[component]
pub fn DashboardPage() -> impl IntoView {
// Check if onboarding is complete
let (show_wizard, set_show_wizard) = signal(false);
let (show_onboarding_section, set_show_onboarding_section) = signal(true);
// Check onboarding status on mount
Effect::new(move |_| {
let onboarding_complete = LocalStorage::get::<bool>("onboarding_complete").unwrap_or(false);
if !onboarding_complete {
set_show_wizard.set(true);
}
set_show_onboarding_section.set(!onboarding_complete);
});
// Reactive signals for service status
let (orchestrator_status, set_orchestrator_status) = signal(String::from("Loading..."));
let (orchestrator_color, set_orchestrator_color) = signal(String::from("#999"));
let (control_center_status, set_control_center_status) = signal(String::from("Loading..."));
let (control_center_color, set_control_center_color) = signal(String::from("#999"));
let (active_tasks, set_active_tasks) = signal(0usize);
// Signals for onboarding status checks
let (workspace_exists, set_workspace_exists) = signal(false);
let (servers_exist, set_servers_exist) = signal(false);
let (taskservs_exist, set_taskservs_exist) = signal(false);
// Fetch orchestrator health
Effect::new(move |_| {
spawn_local(async move {
match client::fetch_orchestrator_health().await {
Ok(health) => {
set_orchestrator_status.set(format!("{}", health.status));
set_orchestrator_color.set(String::from("#10b981"));
}
Err(e) => {
set_orchestrator_status.set(format!("❌ Error: {}", e));
set_orchestrator_color.set(String::from("#ef4444"));
}
}
});
});
// Fetch control-center health
Effect::new(move |_| {
spawn_local(async move {
match client::fetch_control_center_health().await {
Ok(health) => {
set_control_center_status.set(format!("{}", health.status));
set_control_center_color.set(String::from("#10b981"));
}
Err(e) => {
set_control_center_status.set(format!("❌ Error: {}", e));
set_control_center_color.set(String::from("#ef4444"));
}
}
});
});
// Fetch active tasks
Effect::new(move |_| {
spawn_local(async move {
match client::fetch_tasks().await {
Ok(tasks_response) => {
set_active_tasks.set(tasks_response.tasks.len());
}
Err(e) => {
web_sys::console::warn_1(&format!("Failed to fetch tasks: {}", e).into());
set_active_tasks.set(0);
}
}
});
});
// Get config for URLs
let config = use_config();
view! {
// Welcome Wizard (shows on first visit)
<Show when=move || show_wizard.get()>
<WelcomeWizard
on_complete=Callback::new(move |_| {
set_show_wizard.set(false);
set_show_onboarding_section.set(false);
})
on_skip=Callback::new(move |_| {
set_show_wizard.set(false);
})
/>
</Show>
<div class="dashboard-page" style="padding: 20px;">
<h1 style="font-size: 2rem; margin-bottom: 20px; color: #333;">
"📊 Dashboard"
</h1>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 20px;">
// Orchestrator Status Card
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 10px 0; color: #666; font-size: 0.9rem;">
"Orchestrator Status"
</h3>
<p style=move || format!("font-size: 1.5rem; margin: 0; color: {};", orchestrator_color.get())>
{move || orchestrator_status.get()}
</p>
<p style="margin: 10px 0 0 0; color: #999; font-size: 0.85rem;">
{config.api.orchestrator_url.clone()}
</p>
</div>
// Control Center Status Card
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 10px 0; color: #666; font-size: 0.9rem;">
"Control Center Status"
</h3>
<p style=move || format!("font-size: 1.5rem; margin: 0; color: {};", control_center_color.get())>
{move || control_center_status.get()}
</p>
<p style="margin: 10px 0 0 0; color: #999; font-size: 0.85rem;">
{config.api.control_center_url.clone()}
</p>
</div>
// Workflows Card
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 10px 0; color: #666; font-size: 0.9rem;">
"Active Tasks"
</h3>
<p style="font-size: 1.5rem; margin: 0; color: #3b82f6;">
{move || active_tasks.get().to_string()}
</p>
<p style="margin: 10px 0 0 0; color: #999; font-size: 0.85rem;">
{move || if active_tasks.get() == 0 {
"No tasks running"
} else if active_tasks.get() == 1 {
"1 task running"
} else {
"tasks running"
}}
</p>
</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h2 style="margin: 0 0 15px 0; font-size: 1.3rem; color: #333;">
"🚀 Quick Actions"
</h2>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="/servers" style="padding: 10px 20px; background: #3b82f6; color: white; text-decoration: none; border-radius: 6px; font-weight: 500;">
"Manage Servers"
</a>
<a href="/workflows" style="padding: 10px 20px; background: #10b981; color: white; text-decoration: none; border-radius: 6px; font-weight: 500;">
"View Workflows"
</a>
<a href="/taskservs" style="padding: 10px 20px; background: #8b5cf6; color: white; text-decoration: none; border-radius: 6px; font-weight: 500;">
"Task Services"
</a>
</div>
</div>
// Onboarding sections (show if not completed)
<Show when=move || show_onboarding_section.get()>
<div style="margin-top: 30px;">
<SystemStatus auto_refresh=true />
</div>
<div style="margin-top: 30px;">
<NextSteps
workspace_exists=workspace_exists
servers_exist=servers_exist
taskservs_exist=taskservs_exist
/>
</div>
<div style="margin-top: 20px; padding: 15px; background: #fef3c7; border-radius: 8px; border-left: 4px solid #f59e0b;">
<div style="display: flex; align-items: start; gap: 15px;">
<div style="font-size: 1.5rem;">
"💡"
</div>
<div style="flex: 1;">
<p style="margin: 0 0 10px 0; color: #92400e; font-weight: 600;">
"New to the platform?"
</p>
<p style="margin: 0 0 12px 0; color: #78350f; line-height: 1.6;">
"Complete the welcome wizard to set up your first infrastructure. You can restart it anytime from Settings."
</p>
<button
on:click=move |_| {
LocalStorage::delete("onboarding_complete");
set_show_wizard.set(true);
}
style="padding: 8px 16px; background: #f59e0b; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 500;"
>
"Restart Welcome Wizard"
</button>
</div>
</div>
</div>
</Show>
</div>
}
}