diff --git a/src/app.rs b/src/app.rs index 574c909..424338c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -243,11 +243,18 @@ pub fn App() -> Html { 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 local times to UTC for backend storage + let start_local = event_data.start_date.and_time(event_data.start_time); + let end_local = event_data.end_date.and_time(event_data.end_time); + + let start_utc = start_local.and_local_timezone(chrono::Local).unwrap().to_utc(); + let end_utc = end_local.and_local_timezone(chrono::Local).unwrap().to_utc(); + + // Format UTC date and time strings for backend + let start_date = start_utc.format("%Y-%m-%d").to_string(); + let start_time = start_utc.format("%H:%M").to_string(); + let end_date = end_utc.format("%Y-%m-%d").to_string(); + let end_time = end_utc.format("%H:%M").to_string(); // Convert enums to strings for backend let status_str = match event_data.status { @@ -393,6 +400,7 @@ pub fn App() -> Html { on_event_context_menu={Some(on_event_context_menu.clone())} on_calendar_context_menu={Some(on_calendar_date_context_menu.clone())} view={(*current_view).clone()} + on_create_event_request={Some(on_event_create.clone())} /> @@ -406,6 +414,7 @@ pub fn App() -> Html { on_login={on_login.clone()} on_event_context_menu={Some(on_event_context_menu.clone())} on_calendar_context_menu={Some(on_calendar_date_context_menu.clone())} + on_create_event_request={Some(on_event_create.clone())} /> } @@ -651,11 +660,18 @@ pub fn App() -> Html { String::new() }; - // Format date and time strings - let start_date = updated_data.start_date.format("%Y-%m-%d").to_string(); - let start_time = updated_data.start_time.format("%H:%M").to_string(); - let end_date = updated_data.end_date.format("%Y-%m-%d").to_string(); - let end_time = updated_data.end_time.format("%H:%M").to_string(); + // Convert local times to UTC for backend storage + let start_local = updated_data.start_date.and_time(updated_data.start_time); + let end_local = updated_data.end_date.and_time(updated_data.end_time); + + let start_utc = start_local.and_local_timezone(chrono::Local).unwrap().to_utc(); + let end_utc = end_local.and_local_timezone(chrono::Local).unwrap().to_utc(); + + // Format UTC date and time strings for backend + let start_date = start_utc.format("%Y-%m-%d").to_string(); + let start_time = start_utc.format("%H:%M").to_string(); + let end_date = end_utc.format("%Y-%m-%d").to_string(); + let end_time = end_utc.format("%H:%M").to_string(); // Convert enums to strings for backend let status_str = match updated_data.status { diff --git a/src/components/calendar.rs b/src/components/calendar.rs index 5b3a0b6..e95d84f 100644 --- a/src/components/calendar.rs +++ b/src/components/calendar.rs @@ -3,7 +3,7 @@ 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}; +use crate::components::{EventModal, ViewMode, CalendarHeader, MonthView, WeekView, CreateEventModal, EventCreationData}; use gloo_storage::{LocalStorage, Storage}; #[derive(Properties, PartialEq)] @@ -21,6 +21,8 @@ pub struct CalendarProps { pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, + #[prop_or_default] + pub on_create_event_request: Option>, } #[function_component] @@ -58,6 +60,10 @@ pub fn Calendar(props: &CalendarProps) -> Html { }); 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)>); + // Handle view mode changes - adjust current_date format when switching between month/week { let current_date = current_date.clone(); @@ -143,6 +149,18 @@ pub fn Calendar(props: &CalendarProps) -> Html { 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! {
Some("week-view"), _ => None })}> @@ -190,6 +208,7 @@ pub fn Calendar(props: &CalendarProps) -> Html { 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)} /> }, } @@ -205,6 +224,47 @@ pub fn Calendar(props: &CalendarProps) -> Html { }) }} /> + + // Create event modal +
} } \ No newline at end of file diff --git a/src/components/create_event_modal.rs b/src/components/create_event_modal.rs index 89d4bd1..23b3064 100644 --- a/src/components/create_event_modal.rs +++ b/src/components/create_event_modal.rs @@ -12,6 +12,10 @@ pub struct CreateEventModalProps { pub on_create: Callback, pub on_update: Callback<(CalendarEvent, EventCreationData)>, // (original_event, updated_data) pub available_calendars: Vec, + #[prop_or_default] + pub initial_start_time: Option, + #[prop_or_default] + pub initial_end_time: Option, } #[derive(Clone, PartialEq, Debug)] @@ -159,13 +163,16 @@ impl Default for EventCreationData { impl EventCreationData { pub fn from_calendar_event(event: &CalendarEvent) -> Self { // Convert CalendarEvent to EventCreationData for editing + // All events (including temporary drag events) now have proper UTC times + // Convert to local time for display in the modal + Self { title: event.summary.clone().unwrap_or_default(), description: event.description.clone().unwrap_or_default(), - start_date: event.start.date_naive(), - start_time: event.start.time(), - end_date: event.end.as_ref().map(|e| e.date_naive()).unwrap_or(event.start.date_naive()), - end_time: event.end.as_ref().map(|e| e.time()).unwrap_or(event.start.time()), + start_date: event.start.with_timezone(&chrono::Local).date_naive(), + start_time: event.start.with_timezone(&chrono::Local).time(), + end_date: event.end.as_ref().map(|e| e.with_timezone(&chrono::Local).date_naive()).unwrap_or(event.start.with_timezone(&chrono::Local).date_naive()), + end_time: event.end.as_ref().map(|e| e.with_timezone(&chrono::Local).time()).unwrap_or(event.start.with_timezone(&chrono::Local).time()), location: event.location.clone().unwrap_or_default(), all_day: event.all_day, status: EventStatus::from_service_status(&event.status), @@ -187,9 +194,9 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html { let event_data = use_state(|| EventCreationData::default()); // Initialize with selected date or event data if provided - use_effect_with((props.selected_date, props.event_to_edit.clone(), props.is_open, props.available_calendars.clone()), { + use_effect_with((props.selected_date, props.event_to_edit.clone(), props.is_open, props.available_calendars.clone(), props.initial_start_time, props.initial_end_time), { let event_data = event_data.clone(); - move |(selected_date, event_to_edit, is_open, available_calendars)| { + move |(selected_date, event_to_edit, is_open, available_calendars, initial_start_time, initial_end_time)| { if *is_open { let mut data = if let Some(event) = event_to_edit { // Pre-populate with event data for editing @@ -199,6 +206,15 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html { let mut data = EventCreationData::default(); data.start_date = *date; data.end_date = *date; + + // Use initial times if provided (from drag-to-create) + if let Some(start_time) = initial_start_time { + data.start_time = *start_time; + } + if let Some(end_time) = initial_end_time { + data.end_time = *end_time; + } + data } else { // Default initialization diff --git a/src/components/route_handler.rs b/src/components/route_handler.rs index d8cc943..42054e7 100644 --- a/src/components/route_handler.rs +++ b/src/components/route_handler.rs @@ -24,6 +24,8 @@ pub struct RouteHandlerProps { pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, + #[prop_or_default] + pub on_create_event_request: Option>, } #[function_component(RouteHandler)] @@ -34,6 +36,7 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html { let on_event_context_menu = props.on_event_context_menu.clone(); let on_calendar_context_menu = props.on_calendar_context_menu.clone(); let view = props.view.clone(); + let on_create_event_request = props.on_create_event_request.clone(); html! { render={move |route| { @@ -43,6 +46,7 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html { let on_event_context_menu = on_event_context_menu.clone(); let on_calendar_context_menu = on_calendar_context_menu.clone(); let view = view.clone(); + let on_create_event_request = on_create_event_request.clone(); match route { Route::Home => { @@ -67,6 +71,7 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html { on_event_context_menu={on_event_context_menu} on_calendar_context_menu={on_calendar_context_menu} view={view} + on_create_event_request={on_create_event_request} /> } } else { @@ -87,6 +92,8 @@ pub struct CalendarViewProps { pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, + #[prop_or_default] + pub on_create_event_request: Option>, } use gloo_storage::{LocalStorage, Storage}; @@ -246,6 +253,7 @@ pub fn calendar_view(props: &CalendarViewProps) -> Html { on_event_context_menu={props.on_event_context_menu.clone()} on_calendar_context_menu={props.on_calendar_context_menu.clone()} view={props.view.clone()} + on_create_event_request={props.on_create_event_request.clone()} /> } @@ -259,6 +267,7 @@ pub fn calendar_view(props: &CalendarViewProps) -> Html { on_event_context_menu={props.on_event_context_menu.clone()} on_calendar_context_menu={props.on_calendar_context_menu.clone()} view={props.view.clone()} + on_create_event_request={props.on_create_event_request.clone()} /> } } diff --git a/src/components/week_view.rs b/src/components/week_view.rs index f5f97f2..79d4902 100644 --- a/src/components/week_view.rs +++ b/src/components/week_view.rs @@ -397,7 +397,8 @@ fn snap_to_15_minutes(pixels: f64) -> f64 { // Convert pixel position to time (inverse of time to pixels) fn pixels_to_time(pixels: f64) -> NaiveTime { - let total_minutes = (pixels / 60.0) * 60.0; // 60px per hour, 60 minutes per hour + // Since 60px = 1 hour, pixels directly represent minutes + let total_minutes = pixels; // 1px = 1 minute let hours = (total_minutes / 60.0) as u32; let minutes = (total_minutes % 60.0) as u32;