diff --git a/frontend/src/components/torrent/table.rs b/frontend/src/components/torrent/table.rs index cc0119f..9b5ae8d 100644 --- a/frontend/src/components/torrent/table.rs +++ b/frontend/src/components/torrent/table.rs @@ -1,7 +1,7 @@ use leptos::prelude::*; use leptos::task::spawn_local; use std::collections::HashSet; -use icons::{ArrowUpDown, Inbox}; +use icons::{ArrowUpDown, Inbox, Settings2, Play, Square, Trash2, Ellipsis}; use crate::store::{get_action_messages, show_toast}; use crate::api; use shared::NotificationLevel; @@ -9,8 +9,23 @@ use crate::components::context_menu::TorrentContextMenu; use crate::components::ui::card::{Card, CardHeader, CardTitle, CardContent as CardBody}; use crate::components::ui::data_table::*; use crate::components::ui::checkbox::Checkbox; -use crate::components::ui::button::{Button, ButtonVariant}; +use crate::components::ui::button::{Button, ButtonVariant, ButtonSize}; use crate::components::ui::empty::*; +use crate::components::ui::input::Input; +use crate::components::ui::multi_select::*; +use crate::components::ui::dropdown_menu::*; +use crate::components::ui::alert_dialog::*; + +const ALL_COLUMNS: [(&str, &str); 8] = [ + ("Name", "Name"), + ("Size", "Size"), + ("Progress", "Progress"), + ("Status", "Status"), + ("DownSpeed", "DL Speed"), + ("UpSpeed", "UP Speed"), + ("ETA", "ETA"), + ("AddedDate", "Date"), +]; fn format_bytes(bytes: i64) -> String { const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"]; @@ -56,8 +71,13 @@ pub fn TorrentTable() -> impl IntoView { let sort_col = signal(SortColumn::AddedDate); let sort_dir = signal(SortDirection::Descending); - // Multi-selection state let selected_hashes = RwSignal::new(HashSet::::new()); + + let visible_columns = RwSignal::new(HashSet::from([ + "Name".to_string(), "Size".to_string(), "Progress".to_string(), + "Status".to_string(), "DownSpeed".to_string(), "UpSpeed".to_string(), + "ETA".to_string(), "AddedDate".to_string() + ])); let sorted_hashes_data = Memo::new(move |_| { let torrents_map = store.torrents.get(); @@ -116,11 +136,8 @@ pub fn TorrentTable() -> impl IntoView { selected_hashes.update(|selected| { let hashes = filtered_hashes.get_untracked(); for h in hashes { - if checked { - selected.insert(h); - } else { - selected.remove(&h); - } + if checked { selected.insert(h); } + else { selected.remove(&h); } } }); }); @@ -138,14 +155,33 @@ pub fn TorrentTable() -> impl IntoView { let sort_icon = move |col: SortColumn| { let is_active = sort_col.0.get() == col; - let class = if is_active { - "size-3 opacity-100 text-primary" - } else { - "size-3 opacity-30 group-hover:opacity-100 transition-opacity" - }; - view! { - - } + let class = if is_active { "size-3 text-primary" } else { "size-3 opacity-30 group-hover:opacity-100 transition-opacity" }; + view! { } + }; + + let bulk_action = move |action: &'static str| { + let hashes: Vec = selected_hashes.get().into_iter().collect(); + if hashes.is_empty() { return; } + + spawn_local(async move { + let mut success = true; + for hash in hashes { + let res = match action { + "start" => api::torrent::start(&hash).await, + "stop" => api::torrent::stop(&hash).await, + "delete" => api::torrent::delete(&hash).await, + "delete_with_data" => api::torrent::delete_with_data(&hash).await, + _ => Ok(()), + }; + if res.is_err() { success = false; } + } + if success { + show_toast(NotificationLevel::Success, format!("Toplu işlem başarıyla tamamlandı: {}", action)); + selected_hashes.update(|s| s.clear()); + } else { + show_toast(NotificationLevel::Error, "Bazı işlemler başarısız oldu."); + } + }); }; let on_action = Callback::new(move |(action, hash): (String, String)| { @@ -168,15 +204,97 @@ pub fn TorrentTable() -> impl IntoView { }); view! { -
- // --- DESKTOP VIEW --- -