initial commit

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
This commit is contained in:
raf 2026-01-30 22:05:46 +03:00
commit 6a73d11c4b
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
124 changed files with 34856 additions and 0 deletions

View file

@ -0,0 +1,183 @@
use dioxus::prelude::*;
use super::utils::format_size;
use crate::client::LibraryStatisticsResponse;
#[component]
pub fn Statistics(
stats: Option<LibraryStatisticsResponse>,
#[props(default)] error: Option<String>,
on_refresh: EventHandler<()>,
) -> Element {
rsx! {
div { class: "card mb-16",
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! {
// Overview
div { class: "stats-grid",
div { class: "stat-card",
div { class: "stat-value", "{s.total_media}" }
div { class: "stat-label", "Total Media" }
}
div { class: "stat-card",
div { class: "stat-value", "{total_size}" }
div { class: "stat-label", "Total Size" }
}
div { class: "stat-card",
div { class: "stat-value", "{avg_size}" }
div { class: "stat-label", "Avg File Size" }
}
div { class: "stat-card",
div { class: "stat-value", "{s.total_tags}" }
div { class: "stat-label", "Tags" }
}
div { class: "stat-card",
div { class: "stat-value", "{s.total_collections}" }
div { class: "stat-label", "Collections" }
}
div { class: "stat-card",
div { class: "stat-value", "{s.total_duplicates}" }
div { class: "stat-label", "Duplicate Hashes" }
}
}
// Media by Type
if !s.media_by_type.is_empty() {
div { class: "card mt-16",
h4 { class: "card-title", "Media by Type" }
table { class: "table",
thead {
tr {
th { "Type" }
th { "Count" }
}
}
tbody {
for item in s.media_by_type.iter() {
tr {
td { "{item.name}" }
td { "{item.count}" }
}
}
}
}
}
}
// Storage by Type
if !s.storage_by_type.is_empty() {
div { class: "card mt-16",
h4 { class: "card-title", "Storage by Type" }
table { class: "table",
thead {
tr {
th { "Type" }
th { "Size" }
}
}
tbody {
for item in s.storage_by_type.iter() {
tr {
td { "{item.name}" }
td { "{format_size(item.count)}" }
}
}
}
}
}
}
// Top Tags
if !s.top_tags.is_empty() {
div { class: "card mt-16",
h4 { class: "card-title", "Top Tags" }
table { class: "table",
thead {
tr {
th { "Tag" }
th { "Count" }
}
}
tbody {
for item in s.top_tags.iter() {
tr {
td { "{item.name}" }
td { "{item.count}" }
}
}
}
}
}
}
// Top Collections
if !s.top_collections.is_empty() {
div { class: "card mt-16",
h4 { class: "card-title", "Top Collections" }
table { class: "table",
thead {
tr {
th { "Collection" }
th { "Members" }
}
}
tbody {
for item in s.top_collections.iter() {
tr {
td { "{item.name}" }
td { "{item.count}" }
}
}
}
}
}
}
// Date Range
div { class: "card mt-16",
h4 { class: "card-title", "Date Range" }
div { class: "stats-grid",
div { class: "stat-card",
div { class: "stat-value", "{s.oldest_item.as_deref().unwrap_or(\"N/A\")}" }
div { class: "stat-label", "Oldest Item" }
}
div { class: "stat-card",
div { class: "stat-value", "{s.newest_item.as_deref().unwrap_or(\"N/A\")}" }
div { class: "stat-label", "Newest Item" }
}
}
}
}
},
None => rsx! {
div { class: "empty-state",
p { "Loading statistics..." }
}
},
}
}
}
}