From b2f856f80f20b0d2f85d3ae810be3b268840675e Mon Sep 17 00:00:00 2001 From: spinline Date: Fri, 13 Feb 2026 18:36:58 +0300 Subject: [PATCH] feat: implement AutoForm component and refactor Login/Setup screens --- frontend/src/components/auth/login.rs | 77 ++++++++------------ frontend/src/components/auth/setup.rs | 95 ++++++++++--------------- frontend/src/components/ui/auto_form.rs | 93 ++++++++++++++++++++++++ frontend/src/components/ui/mod.rs | 1 + 4 files changed, 160 insertions(+), 106 deletions(-) create mode 100644 frontend/src/components/ui/auto_form.rs diff --git a/frontend/src/components/auth/login.rs b/frontend/src/components/auth/login.rs index 7316352..e07cc35 100644 --- a/frontend/src/components/auth/login.rs +++ b/frontend/src/components/auth/login.rs @@ -1,24 +1,34 @@ use leptos::prelude::*; use leptos::task::spawn_local; use crate::components::ui::card::{Card, CardHeader, CardContent}; -use crate::components::ui::input::{Input, InputType}; - -use crate::components::ui::button::Button; +use crate::components::ui::auto_form::{AutoForm, AutoFormField}; #[component] pub fn Login() -> impl IntoView { - let username = RwSignal::new(String::new()); - let password = RwSignal::new(String::new()); let error = signal(Option::::None); let loading = signal(false); - let handle_login = move |ev: web_sys::SubmitEvent| { - ev.prevent_default(); + let fields = vec![ + AutoFormField::Text { + name: "username".to_string(), + label: "Kullanıcı Adı".to_string(), + placeholder: Some("Kullanıcı adınız".to_string()), + required: true, + }, + AutoFormField::Password { + name: "password".to_string(), + label: "Şifre".to_string(), + placeholder: Some("******".to_string()), + required: true, + }, + ]; + + let on_submit = move |data: std::collections::HashMap| { loading.1.set(true); error.1.set(None); - let user = username.get(); - let pass = password.get(); + let user = data.get("username").cloned().unwrap_or_default(); + let pass = data.get("password").cloned().unwrap_or_default(); spawn_local(async move { match shared::server_fns::auth::login(user, pass).await { @@ -49,47 +59,20 @@ pub fn Login() -> impl IntoView { -
-
- - -
-
- - -
+ - -
- {move || error.0.get().unwrap_or_default()} -
-
- -
- + +
+ {move || error.0.get().unwrap_or_default()}
- +
} -} \ No newline at end of file +} diff --git a/frontend/src/components/auth/setup.rs b/frontend/src/components/auth/setup.rs index 5e3b114..dfc6292 100644 --- a/frontend/src/components/auth/setup.rs +++ b/frontend/src/components/auth/setup.rs @@ -1,23 +1,38 @@ use leptos::prelude::*; use leptos::task::spawn_local; use crate::components::ui::card::{Card, CardHeader, CardContent}; -use crate::components::ui::input::{Input, InputType}; - -use crate::components::ui::button::Button; +use crate::components::ui::auto_form::{AutoForm, AutoFormField}; #[component] pub fn Setup() -> impl IntoView { - 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.get(); - let confirm = confirm_password.get(); + let fields = vec![ + AutoFormField::Text { + name: "username".to_string(), + label: "Yönetici Kullanıcı Adı".to_string(), + placeholder: Some("admin".to_string()), + required: true, + }, + AutoFormField::Password { + name: "password".to_string(), + label: "Şifre".to_string(), + placeholder: Some("******".to_string()), + required: true, + }, + AutoFormField::Password { + name: "confirm_password".to_string(), + label: "Şifre Onay".to_string(), + placeholder: Some("******".to_string()), + required: true, + }, + ]; + + let on_submit = move |data: std::collections::HashMap| { + let user = data.get("username").cloned().unwrap_or_default(); + let pass = data.get("password").cloned().unwrap_or_default(); + let confirm = data.get("confirm_password").cloned().unwrap_or_default(); if pass != confirm { error.1.set(Some("Şifreler eşleşmiyor".to_string())); @@ -32,8 +47,6 @@ pub fn Setup() -> impl IntoView { loading.1.set(true); error.1.set(None); - let user = username.get(); - spawn_local(async move { match shared::server_fns::auth::setup(user, pass).await { Ok(_) => { @@ -64,54 +77,18 @@ 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/ui/auto_form.rs b/frontend/src/components/ui/auto_form.rs new file mode 100644 index 0000000..f86faa9 --- /dev/null +++ b/frontend/src/components/ui/auto_form.rs @@ -0,0 +1,93 @@ +use leptos::prelude::*; +use tailwind_fuse::tw_merge; +use crate::components::ui::button::Button; +use crate::components::ui::input::{Input, InputType}; + +#[derive(Clone, Debug)] +pub enum AutoFormField { + Text { + name: String, + label: String, + placeholder: Option, + required: bool, + }, + Password { + name: String, + label: String, + placeholder: Option, + required: bool, + }, +} + +#[component] +pub fn AutoForm( + #[prop(into)] fields: Vec, + #[prop(into)] submit_label: String, + #[prop(into)] on_submit: Callback>, + #[prop(optional)] loading: Signal, + #[prop(optional, into)] class: String, +) -> impl IntoView { + let field_values = fields.iter().map(|f| { + let name = match f { + AutoFormField::Text { name, .. } => name, + AutoFormField::Password { name, .. } => name, + }; + (name.clone(), RwSignal::new(String::new())) + }).collect::>>(); + + let handle_submit = { + let field_values = field_values.clone(); + move |ev: web_sys::SubmitEvent| { + ev.prevent_default(); + let mut data = std::collections::HashMap::new(); + for (name, signal) in &field_values { + data.insert(name.clone(), signal.get()); + } + on_submit.run(data); + } + }; + + view! { +
+ {fields.into_iter().map(|field| { + let (name, label, placeholder, r#type, required) = match field { + AutoFormField::Text { name, label, placeholder, required } => (name, label, placeholder, InputType::Text, required), + AutoFormField::Password { name, label, placeholder, required } => (name, label, placeholder, InputType::Password, required), + }; + + let signal = field_values.get(&name).cloned().unwrap(); + + view! { +
+ + +
+ } + }).collect_view()} + +
+ +
+
+ } +} diff --git a/frontend/src/components/ui/mod.rs b/frontend/src/components/ui/mod.rs index 884bb2e..535f5d4 100644 --- a/frontend/src/components/ui/mod.rs +++ b/frontend/src/components/ui/mod.rs @@ -1,5 +1,6 @@ pub mod accordion; pub mod alert_dialog; +pub mod auto_form; pub mod badge; pub mod button; pub mod button_action;