Refactor event modal into standalone component
- Created dedicated EventModal component in src/components/event_modal.rs - Extracted modal logic and styling from calendar component for better separation - Updated data flow to pass full CalendarEvent objects instead of strings - Added PartialEq derive to CalendarEvent for component props - Updated service layer to group events by CalendarEvent objects - Enhanced event click handling to show detailed event information - Modal displays title, description, location, start/end times, and status - Maintained existing modal styling and user interaction patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		| @@ -2,7 +2,7 @@ use yew::prelude::*; | ||||
| use yew_router::prelude::*; | ||||
| use gloo_storage::{LocalStorage, Storage}; | ||||
| use crate::components::{Login, Register, Calendar}; | ||||
| use crate::services::CalendarService; | ||||
| use crate::services::{CalendarService, CalendarEvent}; | ||||
| use std::collections::HashMap; | ||||
| use chrono::{Local, NaiveDate, Datelike}; | ||||
|  | ||||
| @@ -107,7 +107,7 @@ pub fn App() -> Html { | ||||
|  | ||||
| #[function_component] | ||||
| fn CalendarView() -> Html { | ||||
|     let events = use_state(|| HashMap::<NaiveDate, Vec<String>>::new()); | ||||
|     let events = use_state(|| HashMap::<NaiveDate, Vec<CalendarEvent>>::new()); | ||||
|     let loading = use_state(|| true); | ||||
|     let error = use_state(|| None::<String>); | ||||
|      | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| use yew::prelude::*; | ||||
| use chrono::{Datelike, Local, NaiveDate, Duration, Weekday}; | ||||
| use std::collections::HashMap; | ||||
| use crate::services::calendar_service::CalendarEvent; | ||||
| use crate::components::EventModal; | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| pub struct CalendarProps { | ||||
|     #[prop_or_default] | ||||
|     pub events: HashMap<NaiveDate, Vec<String>>, | ||||
|     pub events: HashMap<NaiveDate, Vec<CalendarEvent>>, | ||||
| } | ||||
|  | ||||
| #[function_component] | ||||
| @@ -13,6 +15,7 @@ pub fn Calendar(props: &CalendarProps) -> Html { | ||||
|     let today = Local::now().date_naive(); | ||||
|     let current_month = use_state(|| today); | ||||
|     let selected_day = use_state(|| today); | ||||
|     let selected_event = use_state(|| None::<CalendarEvent>); | ||||
|      | ||||
|     let first_day_of_month = current_month.with_day(1).unwrap(); | ||||
|     let days_in_month = get_days_in_month(*current_month); | ||||
| @@ -100,13 +103,23 @@ pub fn Calendar(props: &CalendarProps) -> Html { | ||||
|                                             <div class="event-indicators"> | ||||
|                                                 { | ||||
|                                                     events.iter().take(2).map(|event| { | ||||
|                                                         let event_clone = event.clone(); | ||||
|                                                         let selected_event_clone = selected_event.clone(); | ||||
|                                                         let event_click = Callback::from(move |e: MouseEvent| { | ||||
|                                                             e.stop_propagation(); // Prevent day selection | ||||
|                                                             selected_event_clone.set(Some(event_clone.clone())); | ||||
|                                                         }); | ||||
|                                                          | ||||
|                                                         let title = event.get_title(); | ||||
|                                                         html! {  | ||||
|                                                             <div class="event-box" title={event.clone()}> | ||||
|                                                             <div class="event-box"  | ||||
|                                                                  title={title.clone()}  | ||||
|                                                                  onclick={event_click}> | ||||
|                                                                 { | ||||
|                                                                     if event.len() > 15 { | ||||
|                                                                         format!("{}...", &event[..12]) | ||||
|                                                                     if title.len() > 15 { | ||||
|                                                                         format!("{}...", &title[..12]) | ||||
|                                                                     } else { | ||||
|                                                                         event.clone() | ||||
|                                                                         title | ||||
|                                                                     } | ||||
|                                                                 } | ||||
|                                                             </div>  | ||||
| @@ -133,6 +146,17 @@ pub fn Calendar(props: &CalendarProps) -> Html { | ||||
|                  | ||||
|                 { render_next_month_days(days_from_prev_month.len(), days_in_month) } | ||||
|             </div> | ||||
|              | ||||
|             // 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); | ||||
|                     }) | ||||
|                 }} | ||||
|             /> | ||||
|         </div> | ||||
|     } | ||||
| } | ||||
| @@ -203,4 +227,5 @@ fn get_month_name(month: u32) -> &'static str { | ||||
|         12 => "December", | ||||
|         _ => "Invalid" | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										101
									
								
								src/components/event_modal.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/components/event_modal.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| use yew::prelude::*; | ||||
| use chrono::{DateTime, Utc}; | ||||
| use crate::services::CalendarEvent; | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| pub struct EventModalProps { | ||||
|     pub event: Option<CalendarEvent>, | ||||
|     pub on_close: Callback<()>, | ||||
| } | ||||
|  | ||||
| #[function_component] | ||||
| pub fn EventModal(props: &EventModalProps) -> Html { | ||||
|     let close_modal = { | ||||
|         let on_close = props.on_close.clone(); | ||||
|         Callback::from(move |_| { | ||||
|             on_close.emit(()); | ||||
|         }) | ||||
|     }; | ||||
|      | ||||
|     let backdrop_click = { | ||||
|         let on_close = props.on_close.clone(); | ||||
|         Callback::from(move |e: MouseEvent| { | ||||
|             if e.target() == e.current_target() { | ||||
|                 on_close.emit(()); | ||||
|             } | ||||
|         }) | ||||
|     }; | ||||
|  | ||||
|     if let Some(ref event) = props.event { | ||||
|         html! { | ||||
|             <div class="modal-backdrop" onclick={backdrop_click}> | ||||
|                 <div class="modal-content"> | ||||
|                     <div class="modal-header"> | ||||
|                         <h3>{"Event Details"}</h3> | ||||
|                         <button class="modal-close" onclick={close_modal}>{"×"}</button> | ||||
|                     </div> | ||||
|                     <div class="modal-body"> | ||||
|                         <div class="event-detail"> | ||||
|                             <strong>{"Title:"}</strong> | ||||
|                             <span>{event.get_title()}</span> | ||||
|                         </div> | ||||
|                         { | ||||
|                             if let Some(ref description) = event.description { | ||||
|                                 html! { | ||||
|                                     <div class="event-detail"> | ||||
|                                         <strong>{"Description:"}</strong> | ||||
|                                         <span>{description}</span> | ||||
|                                     </div> | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 html! {} | ||||
|                             } | ||||
|                         } | ||||
|                         { | ||||
|                             if let Some(ref location) = event.location { | ||||
|                                 html! { | ||||
|                                     <div class="event-detail"> | ||||
|                                         <strong>{"Location:"}</strong> | ||||
|                                         <span>{location}</span> | ||||
|                                     </div> | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 html! {} | ||||
|                             } | ||||
|                         } | ||||
|                         <div class="event-detail"> | ||||
|                             <strong>{"Start:"}</strong> | ||||
|                             <span>{format_datetime(&event.start, event.all_day)}</span> | ||||
|                         </div> | ||||
|                         { | ||||
|                             if let Some(ref end) = event.end { | ||||
|                                 html! { | ||||
|                                     <div class="event-detail"> | ||||
|                                         <strong>{"End:"}</strong> | ||||
|                                         <span>{format_datetime(end, event.all_day)}</span> | ||||
|                                     </div> | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 html! {} | ||||
|                             } | ||||
|                         } | ||||
|                         <div class="event-detail"> | ||||
|                             <strong>{"Status:"}</strong> | ||||
|                             <span>{&event.status}</span> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         } | ||||
|     } else { | ||||
|         html! {} | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn format_datetime(dt: &DateTime<Utc>, all_day: bool) -> String { | ||||
|     if all_day { | ||||
|         dt.format("%B %d, %Y").to_string() | ||||
|     } else { | ||||
|         dt.format("%B %d, %Y at %I:%M %p").to_string() | ||||
|     } | ||||
| } | ||||
| @@ -1,7 +1,9 @@ | ||||
| pub mod login; | ||||
| pub mod register; | ||||
| pub mod calendar; | ||||
| pub mod event_modal; | ||||
|  | ||||
| pub use login::Login; | ||||
| pub use register::Register; | ||||
| pub use calendar::Calendar; | ||||
| pub use calendar::Calendar; | ||||
| pub use event_modal::EventModal; | ||||
| @@ -5,7 +5,7 @@ use wasm_bindgen_futures::JsFuture; | ||||
| use web_sys::{Request, RequestInit, RequestMode, Response}; | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||||
| #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] | ||||
| pub struct CalendarEvent { | ||||
|     pub uid: String, | ||||
|     pub summary: Option<String>, | ||||
| @@ -91,16 +91,15 @@ impl CalendarService { | ||||
|     } | ||||
|  | ||||
|     /// Convert events to a HashMap grouped by date for calendar display | ||||
|     pub fn group_events_by_date(events: Vec<CalendarEvent>) -> HashMap<NaiveDate, Vec<String>> { | ||||
|     pub fn group_events_by_date(events: Vec<CalendarEvent>) -> HashMap<NaiveDate, Vec<CalendarEvent>> { | ||||
|         let mut grouped = HashMap::new(); | ||||
|          | ||||
|         for event in events { | ||||
|             let date = event.get_date(); | ||||
|             let title = event.get_title(); | ||||
|              | ||||
|             grouped.entry(date) | ||||
|                 .or_insert_with(Vec::new) | ||||
|                 .push(title); | ||||
|                 .push(event); | ||||
|         } | ||||
|          | ||||
|         grouped | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| pub mod calendar_service; | ||||
|  | ||||
| pub use calendar_service::CalendarService; | ||||
| pub use calendar_service::{CalendarService, CalendarEvent}; | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone