 0453763c98
			
		
	
	0453763c98
	
	
	
		
			
			- Remember checkboxes now default to checked for better user experience - Reduced visual prominence with smaller size, lighter colors, and lower opacity - Users get convenience by default while still being able to opt out 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			292 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use gloo_storage::{LocalStorage, Storage};
 | |
| use web_sys::HtmlInputElement;
 | |
| use yew::prelude::*;
 | |
| 
 | |
| #[derive(Properties, PartialEq)]
 | |
| pub struct LoginProps {
 | |
|     pub on_login: Callback<String>, // Callback with JWT token
 | |
| }
 | |
| 
 | |
| #[function_component]
 | |
| pub fn Login(props: &LoginProps) -> Html {
 | |
|     // Load remembered values from LocalStorage on mount
 | |
|     let server_url = use_state(|| {
 | |
|         LocalStorage::get::<String>("remembered_server_url").unwrap_or_default()
 | |
|     });
 | |
|     let username = use_state(|| {
 | |
|         LocalStorage::get::<String>("remembered_username").unwrap_or_default()
 | |
|     });
 | |
|     let password = use_state(String::new);
 | |
|     let error_message = use_state(|| Option::<String>::None);
 | |
|     let is_loading = use_state(|| false);
 | |
|     
 | |
|     // Remember checkboxes state - default to checked
 | |
|     let remember_server = use_state(|| true);
 | |
|     let remember_username = use_state(|| true);
 | |
| 
 | |
|     let server_url_ref = use_node_ref();
 | |
|     let username_ref = use_node_ref();
 | |
|     let password_ref = use_node_ref();
 | |
| 
 | |
|     let on_server_url_change = {
 | |
|         let server_url = server_url.clone();
 | |
|         Callback::from(move |e: Event| {
 | |
|             let target = e.target_unchecked_into::<HtmlInputElement>();
 | |
|             server_url.set(target.value());
 | |
|         })
 | |
|     };
 | |
| 
 | |
|     let on_username_change = {
 | |
|         let username = username.clone();
 | |
|         Callback::from(move |e: Event| {
 | |
|             let target = e.target_unchecked_into::<HtmlInputElement>();
 | |
|             username.set(target.value());
 | |
|         })
 | |
|     };
 | |
| 
 | |
|     let on_password_change = {
 | |
|         let password = password.clone();
 | |
|         Callback::from(move |e: Event| {
 | |
|             let target = e.target_unchecked_into::<HtmlInputElement>();
 | |
|             password.set(target.value());
 | |
|         })
 | |
|     };
 | |
|     
 | |
|     let on_remember_server_change = {
 | |
|         let remember_server = remember_server.clone();
 | |
|         let server_url = server_url.clone();
 | |
|         Callback::from(move |e: Event| {
 | |
|             let target = e.target_unchecked_into::<HtmlInputElement>();
 | |
|             let checked = target.checked();
 | |
|             remember_server.set(checked);
 | |
|             
 | |
|             if checked {
 | |
|                 let _ = LocalStorage::set("remembered_server_url", (*server_url).clone());
 | |
|             } else {
 | |
|                 let _ = LocalStorage::delete("remembered_server_url");
 | |
|             }
 | |
|         })
 | |
|     };
 | |
|     
 | |
|     let on_remember_username_change = {
 | |
|         let remember_username = remember_username.clone();
 | |
|         let username = username.clone();
 | |
|         Callback::from(move |e: Event| {
 | |
|             let target = e.target_unchecked_into::<HtmlInputElement>();
 | |
|             let checked = target.checked();
 | |
|             remember_username.set(checked);
 | |
|             
 | |
|             if checked {
 | |
|                 let _ = LocalStorage::set("remembered_username", (*username).clone());
 | |
|             } else {
 | |
|                 let _ = LocalStorage::delete("remembered_username");
 | |
|             }
 | |
|         })
 | |
|     };
 | |
| 
 | |
|     let on_submit = {
 | |
|         let server_url = server_url.clone();
 | |
|         let username = username.clone();
 | |
|         let password = password.clone();
 | |
|         let error_message = error_message.clone();
 | |
|         let is_loading = is_loading.clone();
 | |
|         let on_login = props.on_login.clone();
 | |
| 
 | |
|         Callback::from(move |e: SubmitEvent| {
 | |
|             e.prevent_default();
 | |
| 
 | |
|             let server_url = (*server_url).clone();
 | |
|             let username = (*username).clone();
 | |
|             let password = (*password).clone();
 | |
|             let error_message = error_message.clone();
 | |
|             let is_loading = is_loading.clone();
 | |
|             let on_login = on_login.clone();
 | |
| 
 | |
|             // Basic client-side validation
 | |
|             if server_url.trim().is_empty() || username.trim().is_empty() || password.is_empty() {
 | |
|                 error_message.set(Some("Please fill in all fields".to_string()));
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             is_loading.set(true);
 | |
|             error_message.set(None);
 | |
| 
 | |
|             wasm_bindgen_futures::spawn_local(async move {
 | |
|                 web_sys::console::log_1(&"🚀 Starting login process...".into());
 | |
|                 match perform_login(server_url.clone(), username.clone(), password.clone()).await {
 | |
|                     Ok((token, session_token, credentials, preferences)) => {
 | |
|                         web_sys::console::log_1(&"✅ Login successful!".into());
 | |
|                         // Store token and credentials in local storage
 | |
|                         if let Err(_) = LocalStorage::set("auth_token", &token) {
 | |
|                             error_message
 | |
|                                 .set(Some("Failed to store authentication token".to_string()));
 | |
|                             is_loading.set(false);
 | |
|                             return;
 | |
|                         }
 | |
|                         if let Err(_) = LocalStorage::set("session_token", &session_token) {
 | |
|                             error_message
 | |
|                                 .set(Some("Failed to store session token".to_string()));
 | |
|                             is_loading.set(false);
 | |
|                             return;
 | |
|                         }
 | |
|                         if let Err(_) = LocalStorage::set("caldav_credentials", &credentials) {
 | |
|                             error_message.set(Some("Failed to store credentials".to_string()));
 | |
|                             is_loading.set(false);
 | |
|                             return;
 | |
|                         }
 | |
|                         
 | |
|                         // Store preferences from database
 | |
|                         if let Ok(prefs_json) = serde_json::to_string(&preferences) {
 | |
|                             let _ = LocalStorage::set("user_preferences", &prefs_json);
 | |
|                         }
 | |
| 
 | |
|                         is_loading.set(false);
 | |
|                         on_login.emit(token);
 | |
|                     }
 | |
|                     Err(err) => {
 | |
|                         web_sys::console::log_1(&format!("❌ Login failed: {}", err).into());
 | |
|                         error_message.set(Some(err));
 | |
|                         is_loading.set(false);
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
|         })
 | |
|     };
 | |
| 
 | |
|     html! {
 | |
|         <div class="login-container">
 | |
|             <div class="login-form">
 | |
|                 <h2>{"Sign In to CalDAV"}</h2>
 | |
|                 <form onsubmit={on_submit}>
 | |
|                     <div class="form-group">
 | |
|                         <label for="server_url">{"CalDAV Server URL"}</label>
 | |
|                         <input
 | |
|                             ref={server_url_ref}
 | |
|                             type="text"
 | |
|                             id="server_url"
 | |
|                             placeholder="https://your-caldav-server.com/dav/"
 | |
|                             value={(*server_url).clone()}
 | |
|                             onchange={on_server_url_change}
 | |
|                             disabled={*is_loading}
 | |
|                         />
 | |
|                         <div class="remember-checkbox">
 | |
|                             <input
 | |
|                                 type="checkbox"
 | |
|                                 id="remember_server"
 | |
|                                 checked={*remember_server}
 | |
|                                 onchange={on_remember_server_change}
 | |
|                             />
 | |
|                             <label for="remember_server">{"Remember server"}</label>
 | |
|                         </div>
 | |
|                     </div>
 | |
| 
 | |
|                     <div class="form-group">
 | |
|                         <label for="username">{"Username"}</label>
 | |
|                         <input
 | |
|                             ref={username_ref}
 | |
|                             type="text"
 | |
|                             id="username"
 | |
|                             placeholder="Enter your username"
 | |
|                             value={(*username).clone()}
 | |
|                             onchange={on_username_change}
 | |
|                             disabled={*is_loading}
 | |
|                         />
 | |
|                         <div class="remember-checkbox">
 | |
|                             <input
 | |
|                                 type="checkbox"
 | |
|                                 id="remember_username"
 | |
|                                 checked={*remember_username}
 | |
|                                 onchange={on_remember_username_change}
 | |
|                             />
 | |
|                             <label for="remember_username">{"Remember username"}</label>
 | |
|                         </div>
 | |
|                     </div>
 | |
| 
 | |
|                     <div class="form-group">
 | |
|                         <label for="password">{"Password"}</label>
 | |
|                         <input
 | |
|                             ref={password_ref}
 | |
|                             type="password"
 | |
|                             id="password"
 | |
|                             placeholder="Enter your password"
 | |
|                             value={(*password).clone()}
 | |
|                             onchange={on_password_change}
 | |
|                             disabled={*is_loading}
 | |
|                         />
 | |
|                     </div>
 | |
| 
 | |
|                     {
 | |
|                         if let Some(error) = (*error_message).clone() {
 | |
|                             html! { <div class="error-message">{error}</div> }
 | |
|                         } else {
 | |
|                             html! {}
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     <button type="submit" disabled={*is_loading} class="login-button">
 | |
|                         {
 | |
|                             if *is_loading {
 | |
|                                 "Signing in..."
 | |
|                             } else {
 | |
|                                 "Sign In"
 | |
|                             }
 | |
|                         }
 | |
|                     </button>
 | |
|                 </form>
 | |
| 
 | |
|                 <div class="auth-links">
 | |
|                     <p>{"Enter your CalDAV server credentials to connect to your calendar"}</p>
 | |
|                 </div>
 | |
|             </div>
 | |
|         </div>
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Perform login using the CalDAV auth service
 | |
| async fn perform_login(
 | |
|     server_url: String,
 | |
|     username: String,
 | |
|     password: String,
 | |
| ) -> Result<(String, String, String, serde_json::Value), String> {
 | |
|     use crate::auth::{AuthService, CalDAVLoginRequest};
 | |
|     use serde_json;
 | |
| 
 | |
|     web_sys::console::log_1(&format!("📡 Creating auth service and request...").into());
 | |
| 
 | |
|     let auth_service = AuthService::new();
 | |
|     let request = CalDAVLoginRequest {
 | |
|         server_url: server_url.clone(),
 | |
|         username: username.clone(),
 | |
|         password: password.clone(),
 | |
|     };
 | |
| 
 | |
|     web_sys::console::log_1(&format!("🚀 Sending login request to backend...").into());
 | |
| 
 | |
|     match auth_service.login(request).await {
 | |
|         Ok(response) => {
 | |
|             web_sys::console::log_1(&format!("✅ Backend responded successfully").into());
 | |
|             // Create credentials object to store
 | |
|             let credentials = serde_json::json!({
 | |
|                 "server_url": server_url,
 | |
|                 "username": username,
 | |
|                 "password": password
 | |
|             });
 | |
|             
 | |
|             // Extract preferences as JSON
 | |
|             let preferences = serde_json::json!({
 | |
|                 "calendar_selected_date": response.preferences.calendar_selected_date,
 | |
|                 "calendar_time_increment": response.preferences.calendar_time_increment,
 | |
|                 "calendar_view_mode": response.preferences.calendar_view_mode,
 | |
|                 "calendar_theme": response.preferences.calendar_theme,
 | |
|                 "calendar_colors": response.preferences.calendar_colors,
 | |
|             });
 | |
|             
 | |
|             Ok((response.token, response.session_token, credentials.to_string(), preferences))
 | |
|         }
 | |
|         Err(err) => {
 | |
|             web_sys::console::log_1(&format!("❌ Backend error: {}", err).into());
 | |
|             Err(err)
 | |
|         }
 | |
|     }
 | |
| }
 |