use yew::prelude::*; use yew_router::prelude::*; use gloo_storage::{LocalStorage, Storage}; use web_sys::MouseEvent; use crate::components::{Sidebar, CreateCalendarModal, ContextMenu, EventContextMenu, CalendarContextMenu, CreateEventModal, EventCreationData, RouteHandler, EventStatus, EventClass, ReminderType, RecurrenceType}; use crate::services::{CalendarService, calendar_service::{UserInfo, CalendarEvent}}; use chrono::NaiveDate; #[function_component] pub fn App() -> Html { let auth_token = use_state(|| -> Option { LocalStorage::get("auth_token").ok() }); let user_info = use_state(|| -> Option { None }); let color_picker_open = use_state(|| -> Option { None }); let create_modal_open = use_state(|| false); let context_menu_open = use_state(|| false); let context_menu_pos = use_state(|| (0i32, 0i32)); let context_menu_calendar_path = use_state(|| -> Option { None }); let event_context_menu_open = use_state(|| false); let event_context_menu_pos = use_state(|| (0i32, 0i32)); let event_context_menu_event = use_state(|| -> Option { None }); let calendar_context_menu_open = use_state(|| false); let calendar_context_menu_pos = use_state(|| (0i32, 0i32)); let calendar_context_menu_date = use_state(|| -> Option { None }); let create_event_modal_open = use_state(|| false); let selected_date_for_event = use_state(|| -> Option { None }); let available_colors = [ "#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#06B6D4", "#84CC16", "#F97316", "#EC4899", "#6366F1", "#14B8A6", "#F3B806", "#8B5A2B", "#6B7280", "#DC2626", "#7C3AED" ]; let on_login = { let auth_token = auth_token.clone(); Callback::from(move |token: String| { auth_token.set(Some(token)); }) }; let on_logout = { let auth_token = auth_token.clone(); let user_info = user_info.clone(); Callback::from(move |_| { let _ = LocalStorage::delete("auth_token"); auth_token.set(None); user_info.set(None); }) }; // Fetch user info when token is available { let user_info = user_info.clone(); let auth_token = auth_token.clone(); use_effect_with((*auth_token).clone(), move |token| { if let Some(token) = token { let user_info = user_info.clone(); let token = token.clone(); wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; if !password.is_empty() { match calendar_service.fetch_user_info(&token, &password).await { Ok(mut info) => { if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { for saved_cal in &saved_info.calendars { for cal in &mut info.calendars { if cal.path == saved_cal.path { cal.color = saved_cal.color.clone(); } } } } } user_info.set(Some(info)); } Err(err) => { web_sys::console::log_1(&format!("Failed to fetch user info: {}", err).into()); } } } }); } else { user_info.set(None); } || () }); } let on_outside_click = { let color_picker_open = color_picker_open.clone(); let context_menu_open = context_menu_open.clone(); let event_context_menu_open = event_context_menu_open.clone(); let calendar_context_menu_open = calendar_context_menu_open.clone(); Callback::from(move |_: MouseEvent| { color_picker_open.set(None); context_menu_open.set(false); event_context_menu_open.set(false); calendar_context_menu_open.set(false); }) }; let on_color_change = { let user_info = user_info.clone(); let color_picker_open = color_picker_open.clone(); Callback::from(move |(calendar_path, color): (String, String)| { if let Some(mut info) = (*user_info).clone() { for calendar in &mut info.calendars { if calendar.path == calendar_path { calendar.color = color.clone(); break; } } user_info.set(Some(info.clone())); if let Ok(json) = serde_json::to_string(&info) { let _ = LocalStorage::set("calendar_colors", json); } } color_picker_open.set(None); }) }; let on_color_picker_toggle = { let color_picker_open = color_picker_open.clone(); Callback::from(move |calendar_path: String| { if color_picker_open.as_ref() == Some(&calendar_path) { color_picker_open.set(None); } else { color_picker_open.set(Some(calendar_path)); } }) }; let on_calendar_context_menu = { let context_menu_open = context_menu_open.clone(); let context_menu_pos = context_menu_pos.clone(); let context_menu_calendar_path = context_menu_calendar_path.clone(); Callback::from(move |(event, calendar_path): (MouseEvent, String)| { context_menu_open.set(true); context_menu_pos.set((event.client_x(), event.client_y())); context_menu_calendar_path.set(Some(calendar_path)); }) }; let on_event_context_menu = { let event_context_menu_open = event_context_menu_open.clone(); let event_context_menu_pos = event_context_menu_pos.clone(); let event_context_menu_event = event_context_menu_event.clone(); Callback::from(move |(event, calendar_event): (MouseEvent, CalendarEvent)| { event_context_menu_open.set(true); event_context_menu_pos.set((event.client_x(), event.client_y())); event_context_menu_event.set(Some(calendar_event)); }) }; let on_calendar_date_context_menu = { let calendar_context_menu_open = calendar_context_menu_open.clone(); let calendar_context_menu_pos = calendar_context_menu_pos.clone(); let calendar_context_menu_date = calendar_context_menu_date.clone(); Callback::from(move |(event, date): (MouseEvent, NaiveDate)| { calendar_context_menu_open.set(true); calendar_context_menu_pos.set((event.client_x(), event.client_y())); calendar_context_menu_date.set(Some(date)); }) }; let on_create_event_click = { let create_event_modal_open = create_event_modal_open.clone(); let selected_date_for_event = selected_date_for_event.clone(); let calendar_context_menu_date = calendar_context_menu_date.clone(); Callback::from(move |_: MouseEvent| { create_event_modal_open.set(true); selected_date_for_event.set((*calendar_context_menu_date).clone()); }) }; let on_event_create = { let create_event_modal_open = create_event_modal_open.clone(); let auth_token = auth_token.clone(); Callback::from(move |event_data: EventCreationData| { web_sys::console::log_1(&format!("Creating event: {:?}", event_data).into()); create_event_modal_open.set(false); if let Some(token) = (*auth_token).clone() { wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); // Get CalDAV password from storage let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; // Format date and time strings let start_date = event_data.start_date.format("%Y-%m-%d").to_string(); let start_time = event_data.start_time.format("%H:%M").to_string(); let end_date = event_data.end_date.format("%Y-%m-%d").to_string(); let end_time = event_data.end_time.format("%H:%M").to_string(); // Convert enums to strings for backend let status_str = match event_data.status { EventStatus::Tentative => "tentative", EventStatus::Cancelled => "cancelled", _ => "confirmed", }.to_string(); let class_str = match event_data.class { EventClass::Private => "private", EventClass::Confidential => "confidential", _ => "public", }.to_string(); let reminder_str = match event_data.reminder { ReminderType::Minutes15 => "15min", ReminderType::Minutes30 => "30min", ReminderType::Hour1 => "1hour", ReminderType::Hours2 => "2hours", ReminderType::Day1 => "1day", ReminderType::Days2 => "2days", ReminderType::Week1 => "1week", _ => "none", }.to_string(); let recurrence_str = match event_data.recurrence { RecurrenceType::Daily => "daily", RecurrenceType::Weekly => "weekly", RecurrenceType::Monthly => "monthly", RecurrenceType::Yearly => "yearly", _ => "none", }.to_string(); match calendar_service.create_event( &token, &password, event_data.title, event_data.description, start_date, start_time, end_date, end_time, event_data.location, event_data.all_day, status_str, class_str, event_data.priority, event_data.organizer, event_data.attendees, event_data.categories, reminder_str, recurrence_str, event_data.recurrence_days, None // Let backend use first available calendar ).await { Ok(_) => { web_sys::console::log_1(&"Event created successfully".into()); // Refresh the page to show the new event web_sys::window().unwrap().location().reload().unwrap(); } Err(err) => { web_sys::console::error_1(&format!("Failed to create event: {}", err).into()); web_sys::window().unwrap().alert_with_message(&format!("Failed to create event: {}", err)).unwrap(); } } }); } }) }; let refresh_calendars = { let auth_token = auth_token.clone(); let user_info = user_info.clone(); Callback::from(move |_| { if let Some(token) = (*auth_token).clone() { let user_info = user_info.clone(); wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; match calendar_service.fetch_user_info(&token, &password).await { Ok(mut info) => { if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { for saved_cal in &saved_info.calendars { for cal in &mut info.calendars { if cal.path == saved_cal.path { cal.color = saved_cal.color.clone(); } } } } } user_info.set(Some(info)); } Err(err) => { web_sys::console::log_1(&format!("Failed to refresh calendars: {}", err).into()); } } }); } }) }; html! {
{ if auth_token.is_some() { html! { <> >()} on_calendar_context_menu={on_calendar_context_menu} />
} } else { html! { } } } , Option)| { if let Some(token) = (*auth_token).clone() { let refresh_calendars = refresh_calendars.clone(); let create_modal_open = create_modal_open.clone(); wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; match calendar_service.create_calendar(&token, &password, name, description, color).await { Ok(_) => { web_sys::console::log_1(&"Calendar created successfully!".into()); refresh_calendars.emit(()); create_modal_open.set(false); } Err(err) => { web_sys::console::log_1(&format!("Failed to create calendar: {}", err).into()); create_modal_open.set(false); } } }); } } })} available_colors={available_colors.iter().map(|c| c.to_string()).collect::>()} /> ("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; match calendar_service.delete_calendar(&token, &password, calendar_path).await { Ok(_) => { web_sys::console::log_1(&"Calendar deleted successfully!".into()); refresh_calendars.emit(()); } Err(err) => { web_sys::console::log_1(&format!("Failed to delete calendar: {}", err).into()); } } }); } } })} /> ("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() } else { String::new() } } else { String::new() }; if let (Some(calendar_path), Some(event_href)) = (&event.calendar_path, &event.href) { match calendar_service.delete_event(&token, &password, calendar_path.clone(), event_href.clone()).await { Ok(_) => { web_sys::console::log_1(&"Event deleted successfully!".into()); // Close the context menu event_context_menu_open.set(false); // Force a page reload to refresh the calendar events web_sys::window().unwrap().location().reload().unwrap(); } Err(err) => { web_sys::console::log_1(&format!("Failed to delete event: {}", err).into()); } } } else { web_sys::console::log_1(&"Missing calendar_path or href - cannot delete event".into()); } }); } } })} />
} }