feat: complete Phase 0 Enterprise IAM

This commit is contained in:
thakares committed 2026-07-21 15:26:16 +05:30
1 parent 3d2d291006
commit c2f5ba3f54
202 files changed
+21371 -1360

No files matched your search

+318
View File
@@ -0,0 +1,318 @@
//! User and admin dashboards.
use crate::components::feedback::{ErrorState, LoadingSpinner};
use crate::components::navigation::Breadcrumb;
use crate::components::widgets::StatCard;
use crate::routes::Route;
use crate::services::api;
use crate::utils::format_datetime;
use dioxus::prelude::*;
use serde_json::Value;
#[component]
pub fn DashboardPage() -> Element {
let mut data = use_signal(|| Option::<crate::models::DashboardResponse>::None);
let mut error = use_signal(|| Option::<String>::None);
let mut loading = use_signal(|| true);
let load = use_callback(move |_: ()| {
loading.set(true);
error.set(None);
spawn(async move {
match api::dashboard().await {
Ok(d) => {
data.set(Some(d));
loading.set(false);
}
Err(e) => {
error.set(Some(e.to_string()));
loading.set(false);
}
}
});
});
use_effect(move || { load.call(()); });
rsx! {
Breadcrumb { items: vec![("Dashboard".to_string(), None)] }
div { class: "page-header",
div {
h1 { "Dashboard" }
p { class: "desc", "Overview of your identity workspace" }
}
button {
class: "btn btn-outline",
r#type: "button",
onclick: move |_| load.call(()),
"Refresh"
}
}
if loading() {
LoadingSpinner { label: "Loading dashboard…" }
} else if let Some(err) = error() {
ErrorState {
message: err,
on_retry: move |_| load.call(())
}
} else if let Some(d) = data() {
{
let personal = &d.personal;
let username = personal
.pointer("/user/username")
.and_then(|v| v.as_str())
.unwrap_or("user");
let roles = personal
.get("roles")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let sessions = personal
.get("sessions")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let tokens = personal
.get("tokens")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let apps = personal
.get("applications")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let recent = personal
.get("recent_audit")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
rsx! {
// Personal welcome
div { class: "card mb-2",
div { class: "card-body",
h2 { "Welcome, {username}" }
p { class: "text-secondary",
"Roles: "
for (i, r) in roles.iter().enumerate() {
if i > 0 { span { ", " } }
span { class: "badge badge-accent", "{r.as_str().unwrap_or(\"\")}" }
}
if roles.is_empty() {
span { class: "text-muted", "none" }
}
}
}
}
if d.is_admin {
if let Some(admin) = &d.admin {
AdminSummary { admin: admin.clone() }
}
}
div { class: "grid-2",
div { class: "card",
div { class: "card-header", h3 { "Active sessions" } }
div { class: "card-body",
if sessions.is_empty() {
p { class: "text-muted", "No active sessions." }
} else {
div { class: "table-wrap",
table { class: "data-table",
thead {
tr {
th { "IP" }
th { "Last seen" }
th { "Expires" }
}
}
tbody {
for s in sessions.iter().take(5) {
tr {
td { class: "mono",
"{s.get(\"ip_address\").and_then(|v| v.as_str()).unwrap_or(\"—\")}"
}
td {
"{format_datetime(s.get(\"last_seen_at\").and_then(|v| v.as_str()).unwrap_or(\"\"))}"
}
td {
"{format_datetime(s.get(\"expires_at\").and_then(|v| v.as_str()).unwrap_or(\"\"))}"
}
}
}
}
}
}
}
}
}
div { class: "card",
div { class: "card-header",
h3 { "API tokens" }
Link { class: "btn btn-sm btn-outline", to: Route::TokensPage {}, "Manage" }
}
div { class: "card-body",
if tokens.is_empty() {
p { class: "text-muted", "No personal tokens yet." }
} else {
ul { style: "margin:0;padding-left:1.1rem;",
for t in tokens.iter().take(5) {
li {
strong { "{t.get(\"name\").and_then(|v| v.as_str()).unwrap_or(\"token\")}" }
span { class: "text-muted",
" · created {format_datetime(t.get(\"created_at\").and_then(|v| v.as_str()).unwrap_or(\"\"))}"
}
}
}
}
}
}
}
div { class: "card",
div { class: "card-header", h3 { "Applications" } }
div { class: "card-body",
if apps.is_empty() {
p { class: "text-muted", "No applications registered." }
} else {
div { class: "row", style: "flex-wrap:wrap;gap:0.5rem;",
for a in apps {
span { class: "badge badge-info",
"{a.get(\"name\").and_then(|v| v.as_str()).unwrap_or(\"app\")}"
}
}
}
}
}
}
div { class: "card",
div { class: "card-header", h3 { "Recent activity" } }
div { class: "card-body",
if recent.is_empty() {
p { class: "text-muted", "No recent events." }
} else {
div { class: "stack",
for e in recent.iter().take(8) {
div { style: "display:flex;justify-content:space-between;gap:0.5rem;font-size:13px;",
span {
code { "{e.get(\"action\").and_then(|v| v.as_str()).unwrap_or(\"?\")}" }
span { class: "text-muted",
" on {e.get(\"resource_type\").and_then(|v| v.as_str()).unwrap_or(\"?\")}"
}
}
span { class: "text-muted",
"{format_datetime(e.get(\"created_at\").and_then(|v| v.as_str()).unwrap_or(\"\"))}"
}
}
}
}
}
}
}
}
}
}
}
}
}
#[component]
fn AdminSummary(admin: Value) -> Element {
let summary = admin.get("summary").cloned().unwrap_or(Value::Null);
let num = |k: &str| -> String {
summary
.get(k)
.and_then(|v| v.as_i64().or_else(|| v.as_u64().map(|u| u as i64)))
.map(|n| n.to_string())
.unwrap_or_else(|| "—".to_string())
};
let recent_logins = admin
.get("recent_logins")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let recent_users = admin
.get("recent_users")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let health = admin
.get("system_health")
.and_then(|v| v.get("status"))
.and_then(|v| v.as_str())
.unwrap_or("unknown");
rsx! {
div { class: "mb-2",
h2 { style: "margin-bottom: 0.75rem;", "Administrator overview" }
div { class: "stat-grid",
StatCard { label: "Tenants", value: num("tenants"), hint: "".to_string() }
StatCard { label: "Users", value: num("total_users"), hint: "".to_string() }
StatCard { label: "Sessions", value: num("active_sessions"), hint: "".to_string() }
StatCard { label: "Roles", value: num("roles"), hint: "".to_string() }
StatCard { label: "Permissions", value: num("permissions"), hint: "".to_string() }
StatCard { label: "Applications", value: num("applications"), hint: "".to_string() }
StatCard { label: "OAuth Clients", value: num("oauth_clients"), hint: "".to_string() }
StatCard { label: "Service accounts", value: num("service_accounts"), hint: "".to_string() }
StatCard { label: "Audit events", value: num("audit_events"), hint: "".to_string() }
StatCard { label: "Security Alerts", value: num("security_alerts"), hint: "".to_string() }
}
div { class: "grid-2",
div { class: "card",
div { class: "card-header", h3 { "Recent logins" } }
div { class: "card-body",
if recent_logins.is_empty() {
p { class: "text-muted", "No recent logins." }
} else {
for e in recent_logins.iter().take(6) {
div { style: "display:flex;justify-content:space-between;font-size:13px;margin-bottom:0.4rem;",
span { class: "mono",
"{e.get(\"actor_user_id\").and_then(|v| v.as_str()).unwrap_or(\"?\")[..8.min(e.get(\"actor_user_id\").and_then(|v| v.as_str()).unwrap_or(\"\").len())].to_string()}"
}
span { class: "text-muted",
"{format_datetime(e.get(\"created_at\").and_then(|v| v.as_str()).unwrap_or(\"\"))}"
}
}
}
}
}
}
div { class: "card",
div { class: "card-header", h3 { "Recent users" } }
div { class: "card-body",
if recent_users.is_empty() {
p { class: "text-muted", "No users." }
} else {
for u in recent_users.iter().take(6) {
div { style: "display:flex;justify-content:space-between;font-size:13px;margin-bottom:0.4rem;",
span { "{u.get(\"username\").and_then(|v| v.as_str()).unwrap_or(\"?\")}" }
span { class: "badge badge-success",
"{u.get(\"status\").and_then(|v| v.as_str()).unwrap_or(\"\")}"
}
}
}
}
}
}
}
div { class: "card mt-2",
div { class: "card-body row", style: "justify-content:space-between;",
span {
strong { "System health: " }
span { class: "badge badge-success", "{health}" }
}
span { class: "text-muted", "Placeholder probe — expand in a future release" }
}
}
}
}
}