Add calendar selection dropdown and fix multi-calendar event display

- Add calendar selection dropdown to event creation modal
- Update EventCreationData to include selected_calendar field
- Pass available calendars from user info to modal component
- Initialize dropdown with first available calendar as default
- Fix backend to fetch events from ALL calendars, not just the first
- Update refresh_event to search across all calendars
- Events created in any calendar now properly display in UI

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-29 08:45:40 -04:00
parent 7a53228ec8
commit e1578ed11c
3 changed files with 92 additions and 17 deletions

View File

@@ -39,11 +39,24 @@ pub async fn get_calendar_events(
return Ok(Json(vec![])); // No calendars found
}
// Fetch events from the first calendar
let calendar_path = &calendar_paths[0];
let events = client.fetch_events(calendar_path)
.await
.map_err(|e| ApiError::Internal(format!("Failed to fetch events: {}", e)))?;
// Fetch events from all calendars
let mut all_events = Vec::new();
for calendar_path in &calendar_paths {
match client.fetch_events(calendar_path).await {
Ok(mut events) => {
// Set calendar_path for each event to identify which calendar it belongs to
for event in &mut events {
event.calendar_path = Some(calendar_path.clone());
}
all_events.extend(events);
},
Err(e) => {
// Log the error but continue with other calendars
eprintln!("Failed to fetch events from calendar {}: {}", calendar_path, e);
}
}
}
let events = all_events;
// Filter events by month if specified
let filtered_events = if let (Some(year), Some(month)) = (params.year, params.month) {
@@ -80,11 +93,23 @@ pub async fn refresh_event(
return Ok(Json(None)); // No calendars found
}
// Fetch the specific event by UID from the first calendar
let calendar_path = &calendar_paths[0];
let event = client.fetch_event_by_uid(calendar_path, &uid)
.await
.map_err(|e| ApiError::Internal(format!("Failed to fetch event: {}", e)))?;
// Search for the specific event by UID across all calendars
let mut found_event = None;
for calendar_path in &calendar_paths {
match client.fetch_event_by_uid(calendar_path, &uid).await {
Ok(Some(mut event)) => {
event.calendar_path = Some(calendar_path.clone());
found_event = Some(event);
break;
},
Ok(None) => continue, // Event not found in this calendar
Err(e) => {
eprintln!("Failed to fetch event from calendar {}: {}", calendar_path, e);
continue;
}
}
}
let event = found_event;
Ok(Json(event))
}

View File

@@ -273,11 +273,12 @@ pub fn App() -> Html {
reminder_str,
recurrence_str,
event_data.recurrence_days,
None // Let backend use first available calendar
event_data.selected_calendar
).await {
Ok(_) => {
web_sys::console::log_1(&"Event created successfully".into());
// Refresh the page to show the new event
// Trigger a page reload to refresh events from all calendars
// TODO: This could be improved to do a more targeted refresh
web_sys::window().unwrap().location().reload().unwrap();
}
Err(err) => {
@@ -542,6 +543,7 @@ pub fn App() -> Html {
move |_| create_event_modal_open.set(false)
})}
on_create={on_event_create}
available_calendars={user_info.as_ref().map(|ui| ui.calendars.clone()).unwrap_or_default()}
/>
</div>
</BrowserRouter>

View File

@@ -1,6 +1,7 @@
use yew::prelude::*;
use web_sys::{HtmlInputElement, HtmlTextAreaElement, HtmlSelectElement};
use chrono::{NaiveDate, NaiveTime};
use crate::services::calendar_service::CalendarInfo;
#[derive(Properties, PartialEq)]
pub struct CreateEventModalProps {
@@ -8,6 +9,7 @@ pub struct CreateEventModalProps {
pub selected_date: Option<NaiveDate>,
pub on_close: Callback<()>,
pub on_create: Callback<EventCreationData>,
pub available_calendars: Vec<CalendarInfo>,
}
#[derive(Clone, PartialEq, Debug)]
@@ -88,6 +90,7 @@ pub struct EventCreationData {
pub reminder: ReminderType,
pub recurrence: RecurrenceType,
pub recurrence_days: Vec<bool>, // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] for weekly recurrence
pub selected_calendar: Option<String>, // Calendar path
}
impl Default for EventCreationData {
@@ -114,6 +117,7 @@ impl Default for EventCreationData {
reminder: ReminderType::default(),
recurrence: RecurrenceType::default(),
recurrence_days: vec![false; 7], // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] - all false by default
selected_calendar: None,
}
}
}
@@ -123,18 +127,25 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
let event_data = use_state(|| EventCreationData::default());
// Initialize with selected date if provided
use_effect_with((props.selected_date, props.is_open), {
use_effect_with((props.selected_date, props.is_open, props.available_calendars.clone()), {
let event_data = event_data.clone();
move |(selected_date, is_open)| {
move |(selected_date, is_open, available_calendars)| {
if *is_open {
if let Some(date) = selected_date {
let mut data = if let Some(date) = selected_date {
let mut data = (*event_data).clone();
data.start_date = *date;
data.end_date = *date;
event_data.set(data);
data
} else {
event_data.set(EventCreationData::default());
EventCreationData::default()
};
// Set default calendar to the first available one
if data.selected_calendar.is_none() && !available_calendars.is_empty() {
data.selected_calendar = Some(available_calendars[0].path.clone());
}
event_data.set(data);
}
|| ()
}
@@ -164,6 +175,18 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
})
};
let on_calendar_change = {
let event_data = event_data.clone();
Callback::from(move |e: Event| {
if let Some(select) = e.target_dyn_into::<HtmlSelectElement>() {
let mut data = (*event_data).clone();
let value = select.value();
data.selected_calendar = if value.is_empty() { None } else { Some(value) };
event_data.set(data);
}
})
};
let on_description_input = {
let event_data = event_data.clone();
Callback::from(move |e: InputEvent| {
@@ -420,6 +443,31 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
/>
</div>
<div class="form-group">
<label for="event-calendar">{"Calendar"}</label>
<select
id="event-calendar"
class="form-input"
onchange={on_calendar_change}
>
<option value="" selected={data.selected_calendar.is_none()}>{"Select calendar..."}</option>
{
props.available_calendars.iter().map(|calendar| {
let is_selected = data.selected_calendar.as_ref() == Some(&calendar.path);
html! {
<option
key={calendar.path.clone()}
value={calendar.path.clone()}
selected={is_selected}
>
{&calendar.display_name}
</option>
}
}).collect::<Html>()
}
</select>
</div>
<div class="form-group">
<label for="event-description">{"Description"}</label>
<textarea