Fix single event deletion functionality with proper recurring vs non-recurring handling
This commit resolves multiple issues with event deletion: Backend fixes: - Fix CalDAV URL construction for DELETE requests (missing slash separator) - Improve event lookup by href with exact matching and fallback to UID extraction - Add support for both RFC3339 and simple YYYY-MM-DD date formats in occurrence parsing - Implement proper logic to distinguish recurring vs non-recurring events in delete_this action - For non-recurring events: delete entire event from CalDAV server - For recurring events: add EXDATE to exclude specific occurrences - Add comprehensive debug logging for troubleshooting deletion issues Frontend fixes: - Update callback signatures to support series endpoint parameters (7-parameter tuples) - Add update_series method to CalendarService for series-specific operations - Route single occurrence modifications through series endpoint with proper scoping - Fix all component prop definitions to use new callback signature - Update all emit calls to pass correct number of parameters The deletion process now works correctly: - Single events are completely removed from the calendar - Recurring event occurrences are properly excluded via EXDATE - Debug logging helps identify and resolve CalDAV communication issues 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1106,5 +1106,102 @@ impl CalendarService {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_series(
|
||||
&self,
|
||||
token: &str,
|
||||
password: &str,
|
||||
series_uid: String,
|
||||
title: String,
|
||||
description: String,
|
||||
start_date: String,
|
||||
start_time: String,
|
||||
end_date: String,
|
||||
end_time: String,
|
||||
location: String,
|
||||
all_day: bool,
|
||||
status: String,
|
||||
class: String,
|
||||
priority: Option<u8>,
|
||||
organizer: String,
|
||||
attendees: String,
|
||||
categories: String,
|
||||
reminder: String,
|
||||
recurrence: String,
|
||||
calendar_path: Option<String>,
|
||||
update_scope: String,
|
||||
occurrence_date: Option<String>,
|
||||
) -> Result<(), String> {
|
||||
let window = web_sys::window().ok_or("No global window exists")?;
|
||||
|
||||
let opts = RequestInit::new();
|
||||
opts.set_method("POST");
|
||||
opts.set_mode(RequestMode::Cors);
|
||||
|
||||
let body = serde_json::json!({
|
||||
"series_uid": series_uid,
|
||||
"title": title,
|
||||
"description": description,
|
||||
"start_date": start_date,
|
||||
"start_time": start_time,
|
||||
"end_date": end_date,
|
||||
"end_time": end_time,
|
||||
"location": location,
|
||||
"all_day": all_day,
|
||||
"status": status,
|
||||
"class": class,
|
||||
"priority": priority,
|
||||
"organizer": organizer,
|
||||
"attendees": attendees,
|
||||
"categories": categories,
|
||||
"reminder": reminder,
|
||||
"recurrence": recurrence,
|
||||
"recurrence_days": vec![false; 7], // Default - could be enhanced
|
||||
"recurrence_interval": 1_u32, // Default interval
|
||||
"recurrence_end_date": None as Option<String>, // No end date by default
|
||||
"recurrence_count": None as Option<u32>, // No count limit by default
|
||||
"calendar_path": calendar_path,
|
||||
"update_scope": update_scope,
|
||||
"occurrence_date": occurrence_date
|
||||
});
|
||||
|
||||
let url = format!("{}/calendar/events/series/update", self.base_url);
|
||||
|
||||
let body_string = serde_json::to_string(&body)
|
||||
.map_err(|e| format!("JSON serialization failed: {}", e))?;
|
||||
opts.set_body(&body_string.into());
|
||||
let request = Request::new_with_str_and_init(&url, &opts)
|
||||
.map_err(|e| format!("Request creation failed: {:?}", e))?;
|
||||
|
||||
request.headers().set("Authorization", &format!("Bearer {}", token))
|
||||
.map_err(|e| format!("Authorization header setting failed: {:?}", e))?;
|
||||
|
||||
request.headers().set("X-CalDAV-Password", password)
|
||||
.map_err(|e| format!("Password header setting failed: {:?}", e))?;
|
||||
|
||||
request.headers().set("Content-Type", "application/json")
|
||||
.map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;
|
||||
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request))
|
||||
.await
|
||||
.map_err(|e| format!("Network request failed: {:?}", e))?;
|
||||
|
||||
let resp: Response = resp_value.dyn_into()
|
||||
.map_err(|e| format!("Response cast failed: {:?}", e))?;
|
||||
|
||||
let text = JsFuture::from(resp.text()
|
||||
.map_err(|e| format!("Text extraction failed: {:?}", e))?)
|
||||
.await
|
||||
.map_err(|e| format!("Text promise failed: {:?}", e))?;
|
||||
|
||||
let text_string = text.as_string()
|
||||
.ok_or("Response text is not a string")?;
|
||||
|
||||
if resp.ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Request failed with status {}: {}", resp.status(), text_string))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user