use leptos::prelude::*; use leptos_shadcn_tabs::{Tabs, TabsList, TabsTrigger, TabsContent}; fn format_bytes(bytes: i64) -> String { const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"]; if bytes < 1024 { return format!("{} B", bytes); } let i = (bytes as f64).log2().div_euclid(10.0) as usize; format!("{:.1} {}", (bytes as f64) / 1024_f64.powi(i as i32), UNITS[i]) } fn format_speed(bytes_per_sec: i64) -> String { if bytes_per_sec == 0 { return "0 B/s".to_string(); } format!("{}/s", format_bytes(bytes_per_sec)) } fn format_date(timestamp: i64) -> String { if timestamp <= 0 { return "N/A".to_string(); } let dt = chrono::DateTime::from_timestamp(timestamp, 0); match dt { Some(dt) => dt.format("%d/%m/%Y %H:%M").to_string(), None => "N/A".to_string() } } fn format_duration(seconds: i64) -> String { if seconds <= 0 { return "∞".to_string(); } let days = seconds / 86400; let hours = (seconds % 86400) / 3600; let minutes = (seconds % 3600) / 60; let secs = seconds % 60; if days > 0 { format!("{}d {}h", days, hours) } else if hours > 0 { format!("{}h {}m", hours, minutes) } else if minutes > 0 { format!("{}m {}s", minutes, secs) } else { format!("{}s", secs) } } #[component] pub fn TorrentDetail() -> impl IntoView { let store = use_context::().expect("store not provided"); let torrent = Memo::new(move |_| { let hash = store.selected_torrent.get()?; store.torrents.with(|map| map.get(&hash).cloned()) }); let close = move |_| { store.selected_torrent.set(None); }; view! { {move || { let t = torrent.get().unwrap(); let name = t.name.clone(); let status_color = match t.status { shared::TorrentStatus::Seeding => "text-green-500", shared::TorrentStatus::Downloading => "text-blue-500", shared::TorrentStatus::Paused => "text-yellow-500", shared::TorrentStatus::Error => "text-red-500", _ => "text-muted-foreground", }; let status_text = format!("{:?}", t.status); view! {
// Header

{name}

{status_text}
// Tabs
"General" "Transfer" "Files" "Peers"
"File list will be available when file API is connected."
"Peer list will be available when peer API is connected."
} }}
} } #[component] fn DetailItem( #[prop(into)] label: String, #[prop(into)] value: String, ) -> impl IntoView { let title = value.clone(); view! {
{label} {value}
} }