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:
@@ -39,11 +39,24 @@ pub async fn get_calendar_events(
|
|||||||
return Ok(Json(vec![])); // No calendars found
|
return Ok(Json(vec![])); // No calendars found
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch events from the first calendar
|
// Fetch events from all calendars
|
||||||
let calendar_path = &calendar_paths[0];
|
let mut all_events = Vec::new();
|
||||||
let events = client.fetch_events(calendar_path)
|
for calendar_path in &calendar_paths {
|
||||||
.await
|
match client.fetch_events(calendar_path).await {
|
||||||
.map_err(|e| ApiError::Internal(format!("Failed to fetch events: {}", e)))?;
|
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
|
// Filter events by month if specified
|
||||||
let filtered_events = if let (Some(year), Some(month)) = (params.year, params.month) {
|
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
|
return Ok(Json(None)); // No calendars found
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the specific event by UID from the first calendar
|
// Search for the specific event by UID across all calendars
|
||||||
let calendar_path = &calendar_paths[0];
|
let mut found_event = None;
|
||||||
let event = client.fetch_event_by_uid(calendar_path, &uid)
|
for calendar_path in &calendar_paths {
|
||||||
.await
|
match client.fetch_event_by_uid(calendar_path, &uid).await {
|
||||||
.map_err(|e| ApiError::Internal(format!("Failed to fetch event: {}", e)))?;
|
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))
|
Ok(Json(event))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,11 +273,12 @@ pub fn App() -> Html {
|
|||||||
reminder_str,
|
reminder_str,
|
||||||
recurrence_str,
|
recurrence_str,
|
||||||
event_data.recurrence_days,
|
event_data.recurrence_days,
|
||||||
None // Let backend use first available calendar
|
event_data.selected_calendar
|
||||||
).await {
|
).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
web_sys::console::log_1(&"Event created successfully".into());
|
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();
|
web_sys::window().unwrap().location().reload().unwrap();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -542,6 +543,7 @@ pub fn App() -> Html {
|
|||||||
move |_| create_event_modal_open.set(false)
|
move |_| create_event_modal_open.set(false)
|
||||||
})}
|
})}
|
||||||
on_create={on_event_create}
|
on_create={on_event_create}
|
||||||
|
available_calendars={user_info.as_ref().map(|ui| ui.calendars.clone()).unwrap_or_default()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use web_sys::{HtmlInputElement, HtmlTextAreaElement, HtmlSelectElement};
|
use web_sys::{HtmlInputElement, HtmlTextAreaElement, HtmlSelectElement};
|
||||||
use chrono::{NaiveDate, NaiveTime};
|
use chrono::{NaiveDate, NaiveTime};
|
||||||
|
use crate::services::calendar_service::CalendarInfo;
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct CreateEventModalProps {
|
pub struct CreateEventModalProps {
|
||||||
@@ -8,6 +9,7 @@ pub struct CreateEventModalProps {
|
|||||||
pub selected_date: Option<NaiveDate>,
|
pub selected_date: Option<NaiveDate>,
|
||||||
pub on_close: Callback<()>,
|
pub on_close: Callback<()>,
|
||||||
pub on_create: Callback<EventCreationData>,
|
pub on_create: Callback<EventCreationData>,
|
||||||
|
pub available_calendars: Vec<CalendarInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
@@ -88,6 +90,7 @@ pub struct EventCreationData {
|
|||||||
pub reminder: ReminderType,
|
pub reminder: ReminderType,
|
||||||
pub recurrence: RecurrenceType,
|
pub recurrence: RecurrenceType,
|
||||||
pub recurrence_days: Vec<bool>, // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] for weekly recurrence
|
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 {
|
impl Default for EventCreationData {
|
||||||
@@ -114,6 +117,7 @@ impl Default for EventCreationData {
|
|||||||
reminder: ReminderType::default(),
|
reminder: ReminderType::default(),
|
||||||
recurrence: RecurrenceType::default(),
|
recurrence: RecurrenceType::default(),
|
||||||
recurrence_days: vec![false; 7], // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] - all false by 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());
|
let event_data = use_state(|| EventCreationData::default());
|
||||||
|
|
||||||
// Initialize with selected date if provided
|
// 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();
|
let event_data = event_data.clone();
|
||||||
move |(selected_date, is_open)| {
|
move |(selected_date, is_open, available_calendars)| {
|
||||||
if *is_open {
|
if *is_open {
|
||||||
if let Some(date) = selected_date {
|
let mut data = if let Some(date) = selected_date {
|
||||||
let mut data = (*event_data).clone();
|
let mut data = (*event_data).clone();
|
||||||
data.start_date = *date;
|
data.start_date = *date;
|
||||||
data.end_date = *date;
|
data.end_date = *date;
|
||||||
event_data.set(data);
|
data
|
||||||
} else {
|
} 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 on_description_input = {
|
||||||
let event_data = event_data.clone();
|
let event_data = event_data.clone();
|
||||||
Callback::from(move |e: InputEvent| {
|
Callback::from(move |e: InputEvent| {
|
||||||
@@ -420,6 +443,31 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div class="form-group">
|
||||||
<label for="event-description">{"Description"}</label>
|
<label for="event-description">{"Description"}</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
Reference in New Issue
Block a user