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 {
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 {
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}
+
- // --- 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! {
+
+
+ {label}
+ {count}
+
+ }
+}
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
-
-
+
-
"Add Torrent"
"Add"
-
+
// 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(
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;