use dioxus::prelude::*; use dioxus_free_icons::{ Icon, icons::fa_solid_icons::{ FaChartBar, FaCircle, FaClock, FaDatabase, FaFolder, FaLink, FaTags, }, }; use super::utils::format_size; use crate::client::LibraryStatisticsResponse; #[component] pub fn Statistics( stats: Option, #[props(default)] error: Option, on_refresh: EventHandler<()>, ) -> Element { rsx! { div { class: "statistics-page", div { class: "card", div { class: "card-header", h3 { class: "card-title", "Library Statistics" } button { class: "btn btn-sm btn-secondary", onclick: move |_| on_refresh.call(()), "\u{21bb} Refresh" } } if let Some(ref err) = error { div { class: "alert alert-error mb-8", span { "{err}" } button { class: "btn btn-sm btn-secondary ml-8", onclick: move |_| on_refresh.call(()), "Retry" } } } match stats.as_ref() { Some(s) => { let total_size = format_size(s.total_size_bytes); let avg_size = format_size(s.avg_file_size_bytes); rsx! { div { class: "stats-overview", div { class: "stat-card stat-primary", div { class: "stat-icon", Icon { icon: FaFolder, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{s.total_media}" } div { class: "stat-label", "Total Media" } } } div { class: "stat-card stat-success", div { class: "stat-icon", Icon { icon: FaDatabase, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{total_size}" } div { class: "stat-label", "Total Size" } } } div { class: "stat-card stat-info", div { class: "stat-icon", Icon { icon: FaChartBar, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{avg_size}" } div { class: "stat-label", "Average Size" } } } div { class: "stat-card stat-warning", div { class: "stat-icon", Icon { icon: FaTags, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{s.total_tags}" } div { class: "stat-label", "Tags" } } } div { class: "stat-card stat-purple", div { class: "stat-icon", Icon { icon: FaCircle, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{s.total_collections}" } div { class: "stat-label", "Collections" } } } div { class: "stat-card stat-danger", div { class: "stat-icon", Icon { icon: FaLink, width: 20, height: 20 } } div { class: "stat-content", div { class: "stat-value", "{s.total_duplicates}" } div { class: "stat-label", "Duplicates" } } } } if !s.media_by_type.is_empty() { { let max_count = s.media_by_type.iter().map(|i| i.count).max().unwrap_or(1) as f64; rsx! { div { class: "stats-section", h4 { class: "section-title", Icon { icon: FaChartBar, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;", } "Media by Type" } div { class: "chart-bars", for item in s.media_by_type.iter() { { let percentage = (item.count as f64 / max_count) * 100.0; let name = item.name.clone(); let count = item.count; rsx! { div { key: "{name}", class: "bar-item", div { class: "bar-label", "{name}" } div { class: "bar-track", div { class: "bar-fill bar-primary", style: "width: {percentage}%" } } div { class: "bar-value", "{count}" } } } } } } } } } } if !s.storage_by_type.is_empty() { { let max_size = s.storage_by_type.iter().map(|i| i.count).max().unwrap_or(1) as f64; rsx! { div { class: "stats-section", h4 { class: "section-title", Icon { icon: FaDatabase, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;", } "Storage by Type" } div { class: "chart-bars", for item in s.storage_by_type.iter() { { let percentage = (item.count as f64 / max_size) * 100.0; let name = item.name.clone(); let size_str = format_size(item.count); rsx! { div { key: "{name}", class: "bar-item", div { class: "bar-label", "{name}" } div { class: "bar-track", div { class: "bar-fill bar-success", style: "width: {percentage}%" } } div { class: "bar-value", "{size_str}" } } } } } } } } } } if !s.top_tags.is_empty() { div { class: "stats-section", h4 { class: "section-title", Icon { icon: FaTags, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;", } "Top Tags" } div { class: "tag-list", for item in s.top_tags.iter() { div { class: "tag-item", span { class: "tag-badge", "{item.name}" } span { class: "tag-count", "{item.count}" } } } } } } if !s.top_collections.is_empty() { div { class: "stats-section", h4 { class: "section-title", Icon { icon: FaCircle, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;", } "Top Collections" } div { class: "collection-list", for item in s.top_collections.iter() { div { class: "collection-item", Icon { icon: FaFolder, width: 16, height: 16, class: "collection-icon", } span { class: "collection-name", "{item.name}" } span { class: "collection-count", "{item.count}" } } } } } } div { class: "stats-section", h4 { class: "section-title", Icon { icon: FaClock, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;", } "Date Range" } div { class: "date-range", div { class: "date-item", Icon { icon: FaClock, width: 16, height: 16 } div { class: "date-content", div { class: "date-label", "Oldest Item" } div { class: "date-value", "{s.oldest_item.as_deref().unwrap_or(\"N/A\")}" } } } div { class: "date-item", Icon { icon: FaClock, width: 16, height: 16 } div { class: "date-content", div { class: "date-label", "Newest Item" } div { class: "date-value", "{s.newest_item.as_deref().unwrap_or(\"N/A\")}" } } } } } } } None => rsx! { div { class: "empty-state", div { class: "spinner" } p { "Loading statistics..." } } }, } } } } }