 1c0140292f
			
		
	
	1c0140292f
	
	
	
		
			
			Integrate drag-to-create functionality with the full event creation pipeline: - Connect WeekView drag events to CreateEventModal via callback chain - Add event creation request callback through Calendar → RouteHandler → App - Implement proper timezone conversion throughout the entire flow - Fix pixels_to_time calculation (1px = 1 minute, not complex formula) - Add initial_start_time/initial_end_time props to CreateEventModal - Convert local times to UTC in both event creation and update functions - Ensure modal displays correct local times while backend receives UTC - Support both temporary drag events and real server events in modal The complete flow now works: drag selection → modal with correct times → proper UTC conversion → backend storage → correct display in calendar. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			270 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use yew::prelude::*;
 | |
| use chrono::{Datelike, Local, NaiveDate, Duration};
 | |
| use std::collections::HashMap;
 | |
| use web_sys::MouseEvent;
 | |
| use crate::services::calendar_service::{CalendarEvent, UserInfo};
 | |
| 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<NaiveDate, Vec<CalendarEvent>>,
 | |
|     pub on_event_click: Callback<CalendarEvent>,
 | |
|     #[prop_or_default]
 | |
|     pub refreshing_event_uid: Option<String>,
 | |
|     #[prop_or_default]
 | |
|     pub user_info: Option<UserInfo>,
 | |
|     #[prop_or_default]
 | |
|     pub on_event_context_menu: Option<Callback<(web_sys::MouseEvent, CalendarEvent)>>,
 | |
|     #[prop_or_default]
 | |
|     pub on_calendar_context_menu: Option<Callback<(web_sys::MouseEvent, NaiveDate)>>,
 | |
|     #[prop_or_default]
 | |
|     pub view: ViewMode,
 | |
|     #[prop_or_default]
 | |
|     pub on_create_event_request: Option<Callback<EventCreationData>>,
 | |
| }
 | |
| 
 | |
| #[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::<String>("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::<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>);
 | |
|     
 | |
|     // 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)>);
 | |
|     
 | |
|     // 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 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);
 | |
|         })
 | |
|     };
 | |
| 
 | |
|     html! {
 | |
|         <div class={classes!("calendar", match props.view { ViewMode::Week => Some("week-view"), _ => None })}>
 | |
|             <CalendarHeader 
 | |
|                 current_date={*current_date}
 | |
|                 view_mode={props.view.clone()}
 | |
|                 on_prev={on_prev}
 | |
|                 on_next={on_next}
 | |
|                 on_today={on_today}
 | |
|             />
 | |
|             
 | |
|             {
 | |
|                 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! {
 | |
|                             <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
 | |
|                             current_date={*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()}
 | |
|                             on_create_event={Some(on_create_event)}
 | |
|                         />
 | |
|                     },
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Event details modal
 | |
|             <EventModal 
 | |
|                 event={(*selected_event).clone()}
 | |
|                 on_close={{
 | |
|                     let selected_event_clone = selected_event.clone();
 | |
|                     Callback::from(move |_| {
 | |
|                         selected_event_clone.set(None);
 | |
|                     })
 | |
|                 }}
 | |
|             />
 | |
|             
 | |
|             // Create event modal
 | |
|             <CreateEventModal
 | |
|                 is_open={*show_create_modal}
 | |
|                 selected_date={create_event_data.as_ref().map(|(date, _, _)| *date)}
 | |
|                 event_to_edit={None}
 | |
|                 available_calendars={props.user_info.as_ref().map(|info| info.calendars.clone()).unwrap_or_default()}
 | |
|                 initial_start_time={create_event_data.as_ref().map(|(_, start_time, _)| *start_time)}
 | |
|                 initial_end_time={create_event_data.as_ref().map(|(_, _, end_time)| *end_time)}
 | |
|                 on_close={{
 | |
|                     let show_create_modal = show_create_modal.clone();
 | |
|                     let create_event_data = create_event_data.clone();
 | |
|                     Callback::from(move |_| {
 | |
|                         show_create_modal.set(false);
 | |
|                         create_event_data.set(None);
 | |
|                     })
 | |
|                 }}
 | |
|                 on_create={{
 | |
|                     let show_create_modal = show_create_modal.clone();
 | |
|                     let create_event_data = create_event_data.clone();
 | |
|                     let on_create_event_request = props.on_create_event_request.clone();
 | |
|                     Callback::from(move |event_data: EventCreationData| {
 | |
|                         show_create_modal.set(false);
 | |
|                         create_event_data.set(None);
 | |
|                         
 | |
|                         // Emit the create event request to parent
 | |
|                         if let Some(callback) = &on_create_event_request {
 | |
|                             callback.emit(event_data);
 | |
|                         }
 | |
|                     })
 | |
|                 }}
 | |
|                 on_update={{
 | |
|                     let show_create_modal = show_create_modal.clone();
 | |
|                     let create_event_data = create_event_data.clone();
 | |
|                     Callback::from(move |(_original_event, _updated_data): (CalendarEvent, EventCreationData)| {
 | |
|                         show_create_modal.set(false);
 | |
|                         create_event_data.set(None);
 | |
|                         // TODO: Handle actual event update
 | |
|                     })
 | |
|                 }}
 | |
|             />
 | |
|         </div>
 | |
|     }
 | |
| } |