Implement comprehensive calendar UX improvements
- Add time range display to week view events showing "start - end" format - Optimize time display with smart AM/PM formatting to reduce redundancy - Fix context menu overlap by adding stop_propagation to event handlers - Implement persistent view mode (Month/Week) across page refreshes using localStorage - Replace month-based tracking with intelligent selected date tracking - Add day selection in month view with visual feedback and click handlers - Fix view switching to navigate to week containing selected day, not first week of month - Separate selected_date from display_date for proper context switching - Simplify week view header to show "Month Year" instead of "Week of Month Day" - Add backward compatibility for existing localStorage keys Greatly improves calendar navigation and user experience with persistent state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		| @@ -26,66 +26,121 @@ pub struct CalendarProps { | ||||
| #[function_component] | ||||
| pub fn Calendar(props: &CalendarProps) -> Html { | ||||
|     let today = Local::now().date_naive(); | ||||
|     let current_date = use_state(|| { | ||||
|         // Try to load saved date from localStorage | ||||
|         if let Ok(saved_date_str) = LocalStorage::get::<String>("calendar_current_month") { | ||||
|     // 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::<String>("calendar_selected_date") { | ||||
|             if let Ok(saved_date) = NaiveDate::parse_from_str(&saved_date_str, "%Y-%m-%d") { | ||||
|                 saved_date.with_day(1).unwrap_or(today) | ||||
|                 saved_date | ||||
|             } else { | ||||
|                 today | ||||
|             } | ||||
|         } else { | ||||
|             today | ||||
|             // Check for old key for backward compatibility | ||||
|             if let Ok(saved_date_str) = LocalStorage::get::<String>("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::<CalendarEvent>); | ||||
|      | ||||
|     // 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_date = match view { | ||||
|             let (new_selected, new_display) = match view { | ||||
|                 ViewMode::Month => { | ||||
|                     let prev = *current_date - Duration::days(1); | ||||
|                     prev.with_day(1).unwrap() | ||||
|                     // 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) | ||||
|                 }, | ||||
|                 ViewMode::Week => *current_date - Duration::weeks(1), | ||||
|             }; | ||||
|             current_date.set(new_date); | ||||
|             let _ = LocalStorage::set("calendar_current_month", new_date.format("%Y-%m-%d").to_string()); | ||||
|             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_date = match view { | ||||
|             let (new_selected, new_display) = match view { | ||||
|                 ViewMode::Month => { | ||||
|                     if current_date.month() == 12 { | ||||
|                     // 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) | ||||
|                 }, | ||||
|                 ViewMode::Week => *current_date + Duration::weeks(1), | ||||
|             }; | ||||
|             current_date.set(new_date); | ||||
|             let _ = LocalStorage::set("calendar_current_month", new_date.format("%Y-%m-%d").to_string()); | ||||
|             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_date = match view { | ||||
|                 ViewMode::Month => today.with_day(1).unwrap(), | ||||
|                 ViewMode::Week => today, | ||||
|             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 | ||||
|             }; | ||||
|             current_date.set(new_date); | ||||
|             let _ = LocalStorage::set("calendar_current_month", new_date.format("%Y-%m-%d").to_string()); | ||||
|             selected_date.set(new_selected); | ||||
|             current_date.set(new_display); | ||||
|             let _ = LocalStorage::set("calendar_selected_date", new_selected.format("%Y-%m-%d").to_string()); | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
| @@ -101,17 +156,29 @@ pub fn Calendar(props: &CalendarProps) -> Html { | ||||
|              | ||||
|             { | ||||
|                 match props.view { | ||||
|                     ViewMode::Month => html! { | ||||
|                         <MonthView | ||||
|                             current_month={*current_date} | ||||
|                             today={today} | ||||
|                             events={props.events.clone()} | ||||
|                             on_event_click={props.on_event_click.clone()} | ||||
|                             refreshing_event_uid={props.refreshing_event_uid.clone()} | ||||
|                             user_info={props.user_info.clone()} | ||||
|                             on_event_context_menu={props.on_event_context_menu.clone()} | ||||
|                             on_calendar_context_menu={props.on_calendar_context_menu.clone()} | ||||
|                         /> | ||||
|                     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! { | ||||
|                             <MonthView | ||||
|                                 current_month={*current_date} | ||||
|                                 today={today} | ||||
|                                 events={props.events.clone()} | ||||
|                                 on_event_click={props.on_event_click.clone()} | ||||
|                                 refreshing_event_uid={props.refreshing_event_uid.clone()} | ||||
|                                 user_info={props.user_info.clone()} | ||||
|                                 on_event_context_menu={props.on_event_context_menu.clone()} | ||||
|                                 on_calendar_context_menu={props.on_calendar_context_menu.clone()} | ||||
|                                 selected_date={Some(*selected_date)} | ||||
|                                 on_day_select={Some(on_day_select)} | ||||
|                             /> | ||||
|                         } | ||||
|                     }, | ||||
|                     ViewMode::Week => html! { | ||||
|                         <WeekView | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone