use dioxus::prelude::*; use super::utils::{format_size, type_badge_class}; use crate::client::{CollectionResponse, MediaResponse}; #[component] pub fn Collections( collections: Vec, collection_members: Vec, viewing_collection: Option, on_create: EventHandler<(String, String, Option, Option)>, on_delete: EventHandler, on_view_members: EventHandler, on_back_to_list: EventHandler<()>, on_remove_member: EventHandler<(String, String)>, on_select: EventHandler, on_add_member: EventHandler<(String, String)>, all_media: Vec, ) -> Element { let mut new_name = use_signal(String::new); let mut new_kind = use_signal(|| String::from("manual")); let mut new_description = use_signal(String::new); let mut new_filter_query = use_signal(String::new); let mut confirm_delete: Signal> = use_signal(|| None); let mut show_add_modal = use_signal(|| false); // Detail view: viewing a specific collection's members if let Some(ref col_id) = viewing_collection { let col_name = collections .iter() .find(|c| &c.id == col_id) .map(|c| c.name.clone()) .unwrap_or_else(|| col_id.clone()); let back_click = move |_| on_back_to_list.call(()); // Collect IDs of current members to filter available media let member_ids: Vec = collection_members.iter().map(|m| m.id.clone()).collect(); let available_media: Vec<&MediaResponse> = all_media .iter() .filter(|m| !member_ids.contains(&m.id)) .collect(); let modal_col_id = col_id.clone(); return rsx! { button { class: "btn btn-ghost mb-16", onclick: back_click, "\u{2190} Back to Collections" } h3 { class: "mb-16", "{col_name}" } div { class: "form-row mb-16", button { class: "btn btn-primary", onclick: move |_| show_add_modal.set(true), "Add Media" } } if collection_members.is_empty() { div { class: "empty-state", p { class: "empty-subtitle", "This collection has no members." } } } else { table { class: "data-table", thead { tr { th { "Name" } th { "Type" } th { "Artist" } th { "Size" } th { "" } } } tbody { for item in collection_members.iter() { { let artist = item.artist.clone().unwrap_or_default(); let size = format_size(item.file_size); let badge_class = type_badge_class(&item.media_type); let remove_cid = col_id.clone(); let remove_mid = item.id.clone(); let row_click = { let mid = item.id.clone(); move |_| on_select.call(mid.clone()) }; rsx! { tr { key: "{item.id}", class: "clickable-row", onclick: row_click, td { "{item.file_name}" } td { span { class: "type-badge {badge_class}", "{item.media_type}" } } td { "{artist}" } td { "{size}" } td { button { class: "btn btn-danger btn-sm", onclick: move |e: Event| { e.stop_propagation(); on_remove_member.call((remove_cid.clone(), remove_mid.clone())); }, "Remove" } } } } } } } } } // Add Media modal if *show_add_modal.read() { div { class: "modal-overlay", onclick: move |_| show_add_modal.set(false), div { class: "modal", onclick: move |e: Event| e.stop_propagation(), div { class: "modal-header", h3 { "Add Media to Collection" } button { class: "btn btn-ghost", onclick: move |_| show_add_modal.set(false), "\u{2715}" } } div { class: "modal-body", if available_media.is_empty() { p { "No media available to add." } } else { table { class: "data-table", thead { tr { th { "Name" } th { "Type" } th { "Artist" } } } tbody { for media in available_media.iter() { { let artist = media.artist.clone().unwrap_or_default(); let badge_class = type_badge_class(&media.media_type); let add_click = { let cid = modal_col_id.clone(); let mid = media.id.clone(); move |_| { on_add_member.call((cid.clone(), mid.clone())); show_add_modal.set(false); } }; rsx! { tr { key: "{media.id}", class: "clickable-row", onclick: add_click, td { "{media.file_name}" } td { span { class: "type-badge {badge_class}", "{media.media_type}" } } td { "{artist}" } } } } } } } } } } } } }; } // List view: show all collections with create form let is_virtual = *new_kind.read() == "virtual"; let create_click = move |_| { let name = new_name.read().clone(); if name.is_empty() { return; } let kind = new_kind.read().clone(); let desc = { let d = new_description.read().clone(); if d.is_empty() { None } else { Some(d) } }; let filter = { let f = new_filter_query.read().clone(); if f.is_empty() { None } else { Some(f) } }; on_create.call((name, kind, desc, filter)); new_name.set(String::new()); new_kind.set(String::from("manual")); new_description.set(String::new()); new_filter_query.set(String::new()); }; rsx! { div { class: "card", div { class: "card-header", h3 { class: "card-title", "Collections" } } div { class: "form-row mb-16", input { r#type: "text", placeholder: "Collection name...", value: "{new_name}", oninput: move |e| new_name.set(e.value()), } select { value: "{new_kind}", onchange: move |e| new_kind.set(e.value()), option { value: "manual", "Manual" } option { value: "virtual", "Virtual" } } input { r#type: "text", placeholder: "Description (optional)...", value: "{new_description}", oninput: move |e| new_description.set(e.value()), } } if is_virtual { div { class: "form-row mb-16", input { r#type: "text", placeholder: "Filter query for virtual collection...", value: "{new_filter_query}", oninput: move |e| new_filter_query.set(e.value()), } } } div { class: "form-row mb-16", button { class: "btn btn-primary", onclick: create_click, "Create" } } if collections.is_empty() { div { class: "empty-state", p { class: "empty-subtitle", "No collections yet. Create one above." } } } else { table { class: "data-table", thead { tr { th { "Name" } th { "Kind" } th { "Description" } th { "" } th { "" } } } tbody { for col in collections.iter() { { let desc = col.description.clone().unwrap_or_default(); let kind_class = if col.kind == "virtual" { "type-document" } else { "type-other" }; let view_click = { let id = col.id.clone(); move |_| on_view_members.call(id.clone()) }; let col_id_for_delete = col.id.clone(); let is_confirming = confirm_delete .read() .as_ref() .map(|id| id == &col.id) .unwrap_or(false); rsx! { tr { key: "{col.id}", td { "{col.name}" } td { span { class: "type-badge {kind_class}", "{col.kind}" } } td { "{desc}" } td { button { class: "btn btn-sm btn-secondary", onclick: view_click, "View" } } td { if is_confirming { button { class: "btn btn-danger btn-sm", onclick: { let id = col_id_for_delete.clone(); move |_| { on_delete.call(id.clone()); confirm_delete.set(None); } }, "Confirm" } button { class: "btn btn-ghost btn-sm", onclick: move |_| confirm_delete.set(None), "Cancel" } } else { button { class: "btn btn-danger btn-sm", onclick: { let id = col_id_for_delete.clone(); move |_| confirm_delete.set(Some(id.clone())) }, "Delete" } } } } } } } } } } } } }