use yew::prelude::*; use chrono::{Datelike, Local, NaiveDate, Duration}; use std::collections::HashMap; use web_sys::MouseEvent; use crate::services::calendar_service::UserInfo; use crate::models::ical::VEvent; use crate::components::{EventModal, ViewMode, CalendarHeader, MonthView, WeekView, CreateEventModal, EventCreationData}; use gloo_storage::{LocalStorage, Storage}; #[derive(Properties, PartialEq)] pub struct CalendarProps { #[prop_or_default] pub events: HashMap>, pub on_event_click: Callback, #[prop_or_default] pub refreshing_event_uid: Option, #[prop_or_default] pub user_info: Option, #[prop_or_default] pub on_event_context_menu: Option>, #[prop_or_default] pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, #[prop_or_default] pub on_create_event_request: Option>, #[prop_or_default] pub on_event_update_request: Option>)>>, #[prop_or_default] pub context_menus_open: bool, } #[function_component] pub fn Calendar(props: &CalendarProps) -> Html { let today = Local::now().date_naive(); // Track the currently selected date (the actual day the user has selected) let selected_date = use_state(|| { // Try to load saved selected date from localStorage if let Ok(saved_date_str) = LocalStorage::get::("calendar_selected_date") { if let Ok(saved_date) = NaiveDate::parse_from_str(&saved_date_str, "%Y-%m-%d") { saved_date } else { today } } else { // Check for old key for backward compatibility if let Ok(saved_date_str) = LocalStorage::get::("calendar_current_month") { if let Ok(saved_date) = NaiveDate::parse_from_str(&saved_date_str, "%Y-%m-%d") { saved_date } else { today } } else { today } } }); // Track the display date (what to show in the view) let current_date = use_state(|| { match props.view { ViewMode::Month => selected_date.with_day(1).unwrap_or(*selected_date), ViewMode::Week => *selected_date, } }); let selected_event = use_state(|| None::); // State for create event modal let show_create_modal = use_state(|| false); let create_event_data = use_state(|| None::<(chrono::NaiveDate, chrono::NaiveTime, chrono::NaiveTime)>); // State for time increment snapping (15 or 30 minutes) let time_increment = use_state(|| { // Try to load saved time increment from localStorage if let Ok(saved_increment) = LocalStorage::get::("calendar_time_increment") { if saved_increment == 15 || saved_increment == 30 { saved_increment } else { 15 } } else { 15 } }); // Handle view mode changes - adjust current_date format when switching between month/week { let current_date = current_date.clone(); let selected_date = selected_date.clone(); let view = props.view.clone(); use_effect_with(view, move |view_mode| { let selected = *selected_date; let new_display_date = match view_mode { ViewMode::Month => selected.with_day(1).unwrap_or(selected), ViewMode::Week => selected, // Show the week containing the selected date }; current_date.set(new_display_date); || {} }); } let on_prev = { let current_date = current_date.clone(); let selected_date = selected_date.clone(); let view = props.view.clone(); Callback::from(move |_: MouseEvent| { let (new_selected, new_display) = match view { ViewMode::Month => { // Go to previous month, select the 1st day let prev_month = *current_date - Duration::days(1); let first_of_prev = prev_month.with_day(1).unwrap(); (first_of_prev, first_of_prev) }, ViewMode::Week => { // Go to previous week let prev_week = *selected_date - Duration::weeks(1); (prev_week, prev_week) }, }; selected_date.set(new_selected); current_date.set(new_display); let _ = LocalStorage::set("calendar_selected_date", new_selected.format("%Y-%m-%d").to_string()); }) }; let on_next = { let current_date = current_date.clone(); let selected_date = selected_date.clone(); let view = props.view.clone(); Callback::from(move |_: MouseEvent| { let (new_selected, new_display) = match view { ViewMode::Month => { // Go to next month, select the 1st day let next_month = if current_date.month() == 12 { NaiveDate::from_ymd_opt(current_date.year() + 1, 1, 1).unwrap() } else { NaiveDate::from_ymd_opt(current_date.year(), current_date.month() + 1, 1).unwrap() }; (next_month, next_month) }, ViewMode::Week => { // Go to next week let next_week = *selected_date + Duration::weeks(1); (next_week, next_week) }, }; selected_date.set(new_selected); current_date.set(new_display); let _ = LocalStorage::set("calendar_selected_date", new_selected.format("%Y-%m-%d").to_string()); }) }; let on_today = { let current_date = current_date.clone(); let selected_date = selected_date.clone(); let view = props.view.clone(); Callback::from(move |_| { let today = Local::now().date_naive(); let (new_selected, new_display) = match view { ViewMode::Month => { let first_of_today = today.with_day(1).unwrap(); (today, first_of_today) // Select today, but display the month }, ViewMode::Week => (today, today), // Select and display today }; selected_date.set(new_selected); current_date.set(new_display); let _ = LocalStorage::set("calendar_selected_date", new_selected.format("%Y-%m-%d").to_string()); }) }; // Handle time increment toggle let on_time_increment_toggle = { let time_increment = time_increment.clone(); Callback::from(move |_: MouseEvent| { let current = *time_increment; let next = if current == 15 { 30 } else { 15 }; time_increment.set(next); let _ = LocalStorage::set("calendar_time_increment", next); }) }; // Handle drag-to-create event let on_create_event = { let show_create_modal = show_create_modal.clone(); let create_event_data = create_event_data.clone(); Callback::from(move |(_date, start_datetime, end_datetime): (NaiveDate, chrono::NaiveDateTime, chrono::NaiveDateTime)| { // For drag-to-create, we don't need the temporary event approach // Instead, just pass the local times directly via initial_time props create_event_data.set(Some((start_datetime.date(), start_datetime.time(), end_datetime.time()))); show_create_modal.set(true); }) }; // Handle drag-to-move event let on_event_update = { let on_event_update_request = props.on_event_update_request.clone(); Callback::from(move |(event, new_start, new_end, preserve_rrule, until_date): (VEvent, chrono::NaiveDateTime, chrono::NaiveDateTime, bool, Option>)| { if let Some(callback) = &on_event_update_request { callback.emit((event, new_start, new_end, preserve_rrule, until_date)); } }) }; html! {
Some("week-view"), _ => None })}> { match props.view { ViewMode::Month => { let on_day_select = { let selected_date = selected_date.clone(); Callback::from(move |date: NaiveDate| { selected_date.set(date); let _ = LocalStorage::set("calendar_selected_date", date.format("%Y-%m-%d").to_string()); }) }; html! { } }, ViewMode::Week => html! { }, } } // Event details modal // Create event modal
} }