From 7539307e185d49cf47c7c8dcff5d424bbbc109ba Mon Sep 17 00:00:00 2001 From: spinline Date: Wed, 11 Feb 2026 21:01:51 +0300 Subject: [PATCH] feat: shadcn -> rust-ui.com migration + TorrentDetail silme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tüm leptos-shadcn-* paketleri kaldırıldı (19 dependency) - leptos_ui, tw_merge, strum eklendi - components/ui/ modülü oluşturuldu (Button, Card, Input) - TorrentDetail bileşeni tamamen silindi - sidebar.rs: saf HTML+Tailwind ile SidebarButton yardımcı bileşeni - toolbar.rs, login.rs, setup.rs, add_torrent.rs: saf HTML+Tailwind - table.rs: shadcn Card -> rust-ui Card - app.rs: Skeleton -> animate-pulse div --- Cargo.lock | 433 +++--------------- frontend/Cargo.toml | 23 +- frontend/src/app.rs | 40 +- frontend/src/components/auth/login.rs | 49 +- frontend/src/components/auth/setup.rs | 62 ++- frontend/src/components/layout/sidebar.rs | 191 ++++---- frontend/src/components/layout/toolbar.rs | 37 +- frontend/src/components/mod.rs | 1 + .../src/components/torrent/add_torrent.rs | 40 +- frontend/src/components/torrent/detail.rs | 156 ------- frontend/src/components/torrent/mod.rs | 1 - frontend/src/components/torrent/table.rs | 2 +- frontend/src/components/ui/button.rs | 29 ++ frontend/src/components/ui/card.rs | 15 + frontend/src/components/ui/input.rs | 98 ++++ frontend/src/components/ui/mod.rs | 3 + 16 files changed, 415 insertions(+), 765 deletions(-) delete mode 100644 frontend/src/components/torrent/detail.rs create mode 100644 frontend/src/components/ui/button.rs create mode 100644 frontend/src/components/ui/card.rs create mode 100644 frontend/src/components/ui/input.rs create mode 100644 frontend/src/components/ui/mod.rs diff --git a/Cargo.lock b/Cargo.lock index de13562..7887a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,30 +1256,14 @@ dependencies = [ "console_error_panic_hook", "console_log", "futures", + "gloo-console", "gloo-net", "gloo-timers", "js-sys", "leptos", - "leptos-shadcn-alert", - "leptos-shadcn-avatar", - "leptos-shadcn-badge", - "leptos-shadcn-button", - "leptos-shadcn-card", - "leptos-shadcn-context-menu", - "leptos-shadcn-dialog", - "leptos-shadcn-dropdown-menu", - "leptos-shadcn-input", - "leptos-shadcn-label", - "leptos-shadcn-progress", - "leptos-shadcn-scroll-area", - "leptos-shadcn-separator", - "leptos-shadcn-sheet", - "leptos-shadcn-skeleton", - "leptos-shadcn-tabs", - "leptos-shadcn-toast", - "leptos-shadcn-tooltip", "leptos-use", "leptos_router", + "leptos_ui", "log", "rmp-serde", "serde", @@ -1287,8 +1271,10 @@ dependencies = [ "serde_json", "shared", "struct-patch", + "strum", "tailwind_fuse", "thiserror 2.0.18", + "tw_merge", "uuid", "wasm-bindgen", "wasm-bindgen-futures", @@ -1440,6 +1426,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-console" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-net" version = "0.6.0" @@ -2156,334 +2155,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "leptos-node-ref" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f57b1ebc451fe9e7b6c7eba680fa8bc7313b410cc6c0f18481cb55a60ff3ac6" -dependencies = [ - "leptos", - "send_wrapper", -] - -[[package]] -name = "leptos-shadcn-alert" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884551df61ade405bdb61b0d5a92a3a88b3a7af7a2d283b8d3e942ae0d71309c" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-avatar" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb3c5b1f5ba02f7282b55fde1513cdfecef3b25bf5fa44e1eb29fcaf8b927c5" -dependencies = [ - "leptos", - "leptos-shadcn-signal-management", - "leptos-style", - "tailwind_fuse", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-badge" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24578fb0bc21eb21be4e686e6719c7e183acb8fd071a4f81fb27fe452751c88a" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-button" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6d1a7b813b726be7920f7238c127a14129ba4a45fa879312cad3ed2f8a1745" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-card" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5cda16742d1e20284e5f6805eab88b6e54c1378d1548a8e15a5eedda1ea3eb" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-context-menu" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f440e9a7517dfe6ba758080ddba1dfe42e4697008f60adfc112c5da02dca8d" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-dialog" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3fdbb636393b150c2db1e37d44a6832e9dde177ce2e81281932fefad8bde98e" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-dropdown-menu" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50189623a176386a30443d281483c5aa6cc34dc45fa11c3e53bd187ffccde21" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-input" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0939cdad5a878d920decda39a4b42ecf4eba15736a92bbd73b1b408807899b8" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "regex", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-label" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e7cad4b5fae11df9bf3b1d4265a56509a9bb7d3a8580e7487f398b733eadf0c" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-progress" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34ca41b8ebfd7f29126e4f8656987834f3613717016f11f3983da85a90669f6" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-scroll-area" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef3d7bdcae4919ad495529ec2a5974036fb0b959580df310f36b2fd33f90860c" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-separator" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5dfda49f059fd4d1549d663e6743e37a5c6c84d1ac2d6daec32caa3156bc268" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-sheet" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba85819a0c94a7705ed92989442c64cc75d9ed3a4540e711e87c56b206431611" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-signal-management" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5097c5171eb0be12bbf8fd736f4e669012657112865506a825480f2b013f6de" -dependencies = [ - "chrono", - "js-sys", - "leptos", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "leptos-shadcn-skeleton" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c14b6bd0f2fe191e3e114a34cee889fc983546ad488e76e76511e3d75ea3f86" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-tabs" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f817c834e70a8359933b7b274564313be64105370611af96f05508541b661b" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-toast" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3315f7ed844e3286704cc7b57db7209cad592c11eee770f5dc48ebdc92d66cfb" -dependencies = [ - "gloo-timers", - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "uuid", - "web-sys", -] - -[[package]] -name = "leptos-shadcn-tooltip" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e41d37932d700444e1d3a21f10f198c3c9e76dde3fd78d58da4b5a099939fd7" -dependencies = [ - "leptos", - "leptos-node-ref", - "leptos-shadcn-signal-management", - "leptos-struct-component", - "leptos-style", - "tailwind_fuse", - "web-sys", -] - -[[package]] -name = "leptos-struct-component" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c32085b37b67e61e69e0949d94e36c40e4fde83867681cbb884f9cd40a43881e" -dependencies = [ - "leptos", - "leptos-struct-component-macro", -] - -[[package]] -name = "leptos-struct-component-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40efd792acc28a115605b84ecb39e89397a278950bc8f2aad1bdcc7af2033af" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "leptos-style" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65408961a0bd8e70f317de8973d532a0cb9ffbac910c488d97f9c5a2e4411e2" -dependencies = [ - "indexmap", - "leptos", -] - [[package]] name = "leptos-use" version = "0.16.3" @@ -2689,6 +2360,17 @@ dependencies = [ "tachys", ] +[[package]] +name = "leptos_ui" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c30ca85b1aac5637bc59a9201a6aeb648452679bf0ef0e451a8f30acf153f7" +dependencies = [ + "leptos", + "paste", + "tw_merge", +] + [[package]] name = "libc" version = "0.2.180" @@ -3981,6 +3663,7 @@ dependencies = [ "inventory", "js-sys", "pin-project-lite", + "rmp-serde", "rustc_version", "rustversion", "send_wrapper", @@ -4066,6 +3749,7 @@ dependencies = [ "bcrypt", "bytes", "cookie", + "http 1.4.0", "jsonwebtoken", "leptos", "leptos_axum", @@ -4447,6 +4131,28 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.114", +] + [[package]] name = "subtle" version = "2.6.1" @@ -4557,19 +4263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ca71fb01735fbc6fa13e9390d7a3037dde97053c0b65c0c72c0159cd009d26b" dependencies = [ "nom", - "tailwind_fuse_macro", -] - -[[package]] -name = "tailwind_fuse_macro" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa51b9ff80b5533001f8452d254a688bc7bb39c6bb77f9e0a19c1664d035888" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.114", ] [[package]] @@ -4994,6 +4687,28 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tw_merge" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e4ae38c226104e3c821c60b311bca321f45dcf46e48b683a0db2fac9e2c6e2" +dependencies = [ + "nom", + "tw_merge_variants", +] + +[[package]] +name = "tw_merge_variants" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03de956478d5562138828bb736cc066949bda33dbb99c55ef77b2bb5438868e4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "typed-builder" version = "0.21.2" diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 8331bb9..1954622 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -35,22 +35,7 @@ thiserror = "2.0" rmp-serde = "1.3" struct-patch = "0.5" -# ShadCN UI Components (Individual) -leptos-shadcn-button = "0.8" -leptos-shadcn-input = "0.8" -leptos-shadcn-card = "0.8" -leptos-shadcn-badge = "0.8" -leptos-shadcn-context-menu = "0.8" -leptos-shadcn-separator = "0.8" -leptos-shadcn-progress = "0.8" -leptos-shadcn-avatar = "0.8" -leptos-shadcn-sheet = "0.8" -leptos-shadcn-tabs = "0.8" -leptos-shadcn-scroll-area = "0.8" -leptos-shadcn-dialog = "0.8" -leptos-shadcn-label = "0.8" -leptos-shadcn-alert = "0.8" - -leptos-shadcn-dropdown-menu = "0.8" -leptos-shadcn-tooltip = "0.8" -leptos-shadcn-skeleton = "0.8" \ No newline at end of file +# Rust/UI Components +leptos_ui = "0.3" +tw_merge = "0.1" +strum = { version = "0.26", features = ["derive"] } \ No newline at end of file diff --git a/frontend/src/app.rs b/frontend/src/app.rs index 74cc82f..b1f891a 100644 --- a/frontend/src/app.rs +++ b/frontend/src/app.rs @@ -1,13 +1,11 @@ use crate::components::layout::protected::Protected; use crate::components::torrent::table::TorrentTable; -use crate::components::torrent::detail::TorrentDetail; use crate::components::auth::login::Login; use crate::components::auth::setup::Setup; use leptos::prelude::*; use leptos::task::spawn_local; use leptos_router::components::{Router, Routes, Route}; use leptos_router::hooks::use_navigate; -use leptos_shadcn_skeleton::Skeleton; use crate::components::toast::Toaster; #[component] @@ -131,36 +129,33 @@ fn InnerApp() -> impl IntoView {
// Sidebar skeleton
- +
- - - - - - +
+
+
+
+
+
// Main content skeleton
- // Header skeleton
- - -
+
+
+
- // Table skeleton rows
- - - - - - +
+
+
+
+
+
- // Status bar skeleton
- +
@@ -171,7 +166,6 @@ fn InnerApp() -> impl IntoView {
-
diff --git a/frontend/src/components/auth/login.rs b/frontend/src/components/auth/login.rs index 85db36f..b50cb6a 100644 --- a/frontend/src/components/auth/login.rs +++ b/frontend/src/components/auth/login.rs @@ -1,15 +1,12 @@ use leptos::prelude::*; use leptos::task::spawn_local; -use leptos_shadcn_card::{Card, CardHeader, CardContent}; -use leptos_shadcn_input::Input; -use leptos_shadcn_button::Button; -use leptos_shadcn_label::Label; -use leptos_shadcn_alert::{Alert, AlertDescription, AlertVariant}; +use crate::components::ui::card::{Card, CardHeader, CardContent}; +use crate::components::ui::input::{Input, InputType}; #[component] pub fn Login() -> impl IntoView { - let username = signal(String::new()); - let password = signal(String::new()); + let username = RwSignal::new(String::new()); + let password = RwSignal::new(String::new()); let error = signal(Option::::None); let loading = signal(false); @@ -18,8 +15,8 @@ pub fn Login() -> impl IntoView { loading.1.set(true); error.1.set(None); - let user = username.0.get(); - let pass = password.0.get(); + let user = username.get(); + let pass = password.get(); spawn_local(async move { match shared::server_fns::auth::login(user, pass).await { @@ -52,44 +49,40 @@ pub fn Login() -> impl IntoView {
- +
- +
- - - {move || error.0.get().unwrap_or_default()} - - +
+ {move || error.0.get().unwrap_or_default()} +
- +
diff --git a/frontend/src/components/auth/setup.rs b/frontend/src/components/auth/setup.rs index fc49575..10ba0af 100644 --- a/frontend/src/components/auth/setup.rs +++ b/frontend/src/components/auth/setup.rs @@ -1,24 +1,21 @@ use leptos::prelude::*; use leptos::task::spawn_local; -use leptos_shadcn_card::{Card, CardHeader, CardContent}; -use leptos_shadcn_input::Input; -use leptos_shadcn_button::Button; -use leptos_shadcn_label::Label; -use leptos_shadcn_alert::{Alert, AlertDescription, AlertVariant}; +use crate::components::ui::card::{Card, CardHeader, CardContent}; +use crate::components::ui::input::{Input, InputType}; #[component] pub fn Setup() -> impl IntoView { - let username = signal(String::new()); - let password = signal(String::new()); - let confirm_password = signal(String::new()); + let username = RwSignal::new(String::new()); + let password = RwSignal::new(String::new()); + let confirm_password = RwSignal::new(String::new()); let error = signal(Option::::None); let loading = signal(false); let handle_setup = move |ev: web_sys::SubmitEvent| { ev.prevent_default(); - let pass = password.0.get(); - let confirm = confirm_password.0.get(); + let pass = password.get(); + let confirm = confirm_password.get(); if pass != confirm { error.1.set(Some("Şifreler eşleşmiyor".to_string())); @@ -33,7 +30,7 @@ pub fn Setup() -> impl IntoView { loading.1.set(true); error.1.set(None); - let user = username.0.get(); + let user = username.get(); spawn_local(async move { match shared::server_fns::auth::setup(user, pass).await { @@ -67,54 +64,49 @@ pub fn Setup() -> impl IntoView {
- +
- +
- +
- - - {move || error.0.get().unwrap_or_default()} - - +
+ {move || error.0.get().unwrap_or_default()} +
- +
diff --git a/frontend/src/components/layout/sidebar.rs b/frontend/src/components/layout/sidebar.rs index 9857398..5a5fe4b 100644 --- a/frontend/src/components/layout/sidebar.rs +++ b/frontend/src/components/layout/sidebar.rs @@ -1,8 +1,5 @@ use leptos::prelude::*; use leptos::task::spawn_local; -use leptos_shadcn_button::{Button, ButtonVariant, ButtonSize}; -use leptos_shadcn_avatar::{Avatar, AvatarFallback}; -use leptos_shadcn_separator::Separator; use leptos_use::storage::use_local_storage; use ::codee::string::FromToStringCodec; @@ -84,7 +81,6 @@ pub fn Sidebar() -> impl IntoView { let theme = current_theme.get().to_lowercase(); if let Some(doc) = document().document_element() { let _ = doc.set_attribute("data-theme", &theme); - // Also set class for Shadcn dark mode support if theme == "dark" || theme == "dracula" || theme == "dim" || theme == "abyss" || theme == "sunset" || theme == "cyberpunk" || theme == "nord" || theme == "business" || theme == "night" || theme == "black" || theme == "luxury" || theme == "coffee" || theme == "forest" || theme == "halloween" || theme == "synthwave" { let _ = doc.class_list().add_1("dark"); } else { @@ -93,8 +89,6 @@ pub fn Sidebar() -> impl IntoView { } }); - - let toggle_theme = move |_| { let new_theme = if current_theme.get() == "dark" { "light" } else { "dark" }; set_current_theme.set(new_theme.to_string()); @@ -110,110 +104,70 @@ pub fn Sidebar() -> impl IntoView {

"Filters"

- - - - - - - - - - - + + + + + +
- + // Separator +
- - - {first_letter} - - + // Avatar +
+ {first_letter} +
{username}
"Online"
- // --- THEME BUTTON --- - - +
} } + +#[component] +fn SidebarButton( + active: Signal, + on_click: impl Fn(web_sys::MouseEvent) + 'static, + #[prop(into)] icon: String, + #[prop(into)] label: &'static str, + count: Signal, +) -> impl IntoView { + view! { + + } +} diff --git a/frontend/src/components/layout/toolbar.rs b/frontend/src/components/layout/toolbar.rs index b086ff3..c5307de 100644 --- a/frontend/src/components/layout/toolbar.rs +++ b/frontend/src/components/layout/toolbar.rs @@ -1,6 +1,4 @@ use leptos::prelude::*; -use leptos_shadcn_input::Input; -use leptos_shadcn_button::{Button, ButtonVariant, ButtonSize}; use crate::components::torrent::add_torrent::AddTorrentDialog; #[component] @@ -9,30 +7,36 @@ pub fn Toolbar() -> impl IntoView { let store = use_context::().expect("store not provided"); let is_mobile_menu_open = use_context::>().expect("mobile menu state not provided"); + let search_value = RwSignal::new(String::new()); + + // Sync search_value to store + Effect::new(move |_| { + let val = search_value.get(); + store.search_query.set(val); + }); + view! {
// Sol kısım: Menü butonu + Add Torrent
// Mobile Menu Trigger - + - +
// Sağ kısım: Search kutusu @@ -42,12 +46,11 @@ pub fn Toolbar() -> impl IntoView { -
diff --git a/frontend/src/components/mod.rs b/frontend/src/components/mod.rs index 2cb44a6..7541e03 100644 --- a/frontend/src/components/mod.rs +++ b/frontend/src/components/mod.rs @@ -3,3 +3,4 @@ pub mod layout; pub mod torrent; pub mod auth; pub mod toast; +pub mod ui; diff --git a/frontend/src/components/torrent/add_torrent.rs b/frontend/src/components/torrent/add_torrent.rs index 55efaa6..0b0d9fd 100644 --- a/frontend/src/components/torrent/add_torrent.rs +++ b/frontend/src/components/torrent/add_torrent.rs @@ -1,8 +1,6 @@ use leptos::prelude::*; use leptos::task::spawn_local; -use leptos_shadcn_input::Input; -use leptos_shadcn_button::{Button, ButtonVariant}; -use leptos_shadcn_alert::{Alert, AlertDescription, AlertVariant}; +use crate::components::ui::input::{Input, InputType}; use crate::store::TorrentStore; use crate::api; @@ -12,13 +10,13 @@ pub fn AddTorrentDialog( ) -> impl IntoView { let _store = use_context::().expect("TorrentStore not provided"); - let uri = signal(String::new()); + let uri = RwSignal::new(String::new()); let is_loading = signal(false); let error_msg = signal(Option::::None); let handle_submit = move |ev: web_sys::SubmitEvent| { ev.prevent_default(); - let uri_val = uri.0.get(); + let uri_val = uri.get(); if uri_val.is_empty() { error_msg.1.set(Some("Please enter a Magnet URI or URL".to_string())); @@ -69,29 +67,31 @@ pub fn AddTorrentDialog(
{move || error_msg.0.get().map(|msg| view! { - - {msg} - +
+ {msg} +
})}
- - + +
diff --git a/frontend/src/components/torrent/detail.rs b/frontend/src/components/torrent/detail.rs deleted file mode 100644 index 615ce8a..0000000 --- a/frontend/src/components/torrent/detail.rs +++ /dev/null @@ -1,156 +0,0 @@ -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} -
- } -} diff --git a/frontend/src/components/torrent/mod.rs b/frontend/src/components/torrent/mod.rs index 50f6a93..438f1c3 100644 --- a/frontend/src/components/torrent/mod.rs +++ b/frontend/src/components/torrent/mod.rs @@ -1,3 +1,2 @@ pub mod table; pub mod add_torrent; -pub mod detail; diff --git a/frontend/src/components/torrent/table.rs b/frontend/src/components/torrent/table.rs index fe06599..5ccb701 100644 --- a/frontend/src/components/torrent/table.rs +++ b/frontend/src/components/torrent/table.rs @@ -4,7 +4,7 @@ use crate::store::{get_action_messages, show_toast}; use crate::api; use shared::NotificationLevel; use crate::components::context_menu::TorrentContextMenu; -use leptos_shadcn_card::{Card, CardHeader, CardTitle, CardContent}; +use crate::components::ui::card::{Card, CardHeader, CardTitle, CardContent}; fn format_bytes(bytes: i64) -> String { const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"]; diff --git a/frontend/src/components/ui/button.rs b/frontend/src/components/ui/button.rs new file mode 100644 index 0000000..43f364d --- /dev/null +++ b/frontend/src/components/ui/button.rs @@ -0,0 +1,29 @@ +use leptos::prelude::*; +use leptos_ui::variants; + +variants! { + Button { + base: "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive w-fit hover:cursor-pointer active:scale-[0.98] active:opacity-100 touch-manipulation [-webkit-tap-highlight-color:transparent] select-none [-webkit-touch-callout:none]", + variants: { + variant: { + Default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + Destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + Outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/5", + Secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + Ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + Link: "text-primary underline-offset-4 hover:underline", + }, + size: { + Default: "h-9 px-4 py-2 has-[>svg]:px-3", + Sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + Lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + Icon: "size-9", + } + }, + component: { + element: button, + support_href: true, + support_aria_current: true + } + } +} diff --git a/frontend/src/components/ui/card.rs b/frontend/src/components/ui/card.rs new file mode 100644 index 0000000..149c0dd --- /dev/null +++ b/frontend/src/components/ui/card.rs @@ -0,0 +1,15 @@ +use leptos::prelude::*; +use leptos_ui::clx; + +mod components { + use super::*; + + clx! {Card, div, "bg-card text-card-foreground flex flex-col gap-4 rounded-xl border py-6 shadow-sm"} + clx! {CardHeader, div, "@container/card-header flex flex-col items-start gap-1.5 px-6 [.border-b]:pb-6"} + clx! {CardTitle, h2, "leading-none font-semibold"} + clx! {CardContent, div, "px-6"} + clx! {CardDescription, p, "text-muted-foreground text-sm"} + clx! {CardFooter, footer, "flex items-center px-6 [.border-t]:pt-6", "gap-2"} +} + +pub use components::*; diff --git a/frontend/src/components/ui/input.rs b/frontend/src/components/ui/input.rs new file mode 100644 index 0000000..cd3660a --- /dev/null +++ b/frontend/src/components/ui/input.rs @@ -0,0 +1,98 @@ +use leptos::html; +use leptos::prelude::*; +use strum::AsRefStr; +use tw_merge::tw_merge; + +#[derive(Default, Clone, Copy, PartialEq, Eq, AsRefStr)] +#[strum(serialize_all = "lowercase")] +pub enum InputType { + #[default] + Text, + Email, + Password, + Number, + Tel, + Url, + Search, + Date, + Time, + #[strum(serialize = "datetime-local")] + DatetimeLocal, + Month, + Week, + Color, + File, + Hidden, +} + +#[component] +pub fn Input( + #[prop(into, optional)] class: String, + #[prop(default = InputType::default())] r#type: InputType, + #[prop(into, optional)] placeholder: Option, + #[prop(into, optional)] name: Option, + #[prop(into, optional)] id: Option, + #[prop(into, optional)] title: Option, + #[prop(optional)] disabled: bool, + #[prop(optional)] readonly: bool, + #[prop(optional)] required: bool, + #[prop(optional)] autofocus: bool, + #[prop(into, optional)] min: Option, + #[prop(into, optional)] max: Option, + #[prop(into, optional)] step: Option, + #[prop(into, optional)] bind_value: Option>, + #[prop(optional)] node_ref: NodeRef, +) -> impl IntoView { + let merged_class = tw_merge!( + "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + "focus-visible:border-ring focus-visible:ring-ring/50", + "focus-visible:ring-2", + "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "read-only:bg-muted", + class + ); + + let type_str = r#type.as_ref(); + + match bind_value { + Some(signal) => view! { + + }.into_any(), + None => view! { + + }.into_any(), + } +} diff --git a/frontend/src/components/ui/mod.rs b/frontend/src/components/ui/mod.rs new file mode 100644 index 0000000..546f63a --- /dev/null +++ b/frontend/src/components/ui/mod.rs @@ -0,0 +1,3 @@ +pub mod button; +pub mod card; +pub mod input;