Implement calendar deletion with right-click context menu

Added complete calendar deletion functionality including:
- Context menu component with right-click activation on calendar items
- Backend API endpoint for calendar deletion with CalDAV DELETE method
- Frontend integration with calendar list refresh after deletion
- Fixed URL construction to prevent double /dav.php path issue
- Added proper error handling and user feedback

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-28 21:31:58 -04:00
parent f9c87369e5
commit c454104c69
9 changed files with 341 additions and 5 deletions

View File

@@ -658,6 +658,42 @@ impl CalDAVClient {
Err(CalDAVError::ServerError(status.as_u16()))
}
}
/// Delete a calendar from the CalDAV server
pub async fn delete_calendar(&self, calendar_path: &str) -> Result<(), CalDAVError> {
let full_url = if calendar_path.starts_with("http") {
calendar_path.to_string()
} else {
// Handle case where calendar_path already contains /dav.php
let clean_path = if calendar_path.starts_with("/dav.php") {
calendar_path.trim_start_matches("/dav.php")
} else {
calendar_path
};
format!("{}{}", self.config.server_url.trim_end_matches('/'), clean_path)
};
println!("Deleting calendar at: {}", full_url);
let response = self.http_client
.delete(&full_url)
.header("Authorization", format!("Basic {}", self.config.get_basic_auth()))
.send()
.await
.map_err(|e| CalDAVError::ParseError(e.to_string()))?;
println!("Calendar deletion response status: {}", response.status());
if response.status().is_success() || response.status().as_u16() == 204 {
println!("✅ Calendar deleted successfully at {}", calendar_path);
Ok(())
} else {
let status = response.status();
let error_body = response.text().await.unwrap_or_default();
println!("❌ Calendar deletion failed: {} - {}", status, error_body);
Err(CalDAVError::ServerError(status.as_u16()))
}
}
}
/// Helper struct for extracting calendar data from XML responses

View File

@@ -7,7 +7,7 @@ use serde::Deserialize;
use std::sync::Arc;
use chrono::Datelike;
use crate::{AppState, models::{CalDAVLoginRequest, AuthResponse, ApiError, UserInfo, CalendarInfo, CreateCalendarRequest, CreateCalendarResponse}};
use crate::{AppState, models::{CalDAVLoginRequest, AuthResponse, ApiError, UserInfo, CalendarInfo, CreateCalendarRequest, CreateCalendarResponse, DeleteCalendarRequest, DeleteCalendarResponse}};
use crate::calendar::{CalDAVClient, CalendarEvent};
#[derive(Deserialize)]
@@ -292,4 +292,35 @@ pub async fn create_calendar(
success: true,
message: "Calendar created successfully".to_string(),
}))
}
pub async fn delete_calendar(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Json(request): Json<DeleteCalendarRequest>,
) -> Result<Json<DeleteCalendarResponse>, ApiError> {
println!("🗑️ Delete calendar request received: path='{}'", request.path);
// Extract and verify token
let token = extract_bearer_token(&headers)?;
let password = extract_password_header(&headers)?;
// Validate request
if request.path.trim().is_empty() {
return Err(ApiError::BadRequest("Calendar path is required".to_string()));
}
// Create CalDAV config from token and password
let config = state.auth_service.caldav_config_from_token(&token, &password)?;
let client = CalDAVClient::new(config);
// Delete the calendar
client.delete_calendar(&request.path)
.await
.map_err(|e| ApiError::Internal(format!("Failed to delete calendar: {}", e)))?;
Ok(Json(DeleteCalendarResponse {
success: true,
message: "Calendar deleted successfully".to_string(),
}))
}

View File

@@ -39,6 +39,7 @@ pub async fn run_server() -> Result<(), Box<dyn std::error::Error>> {
.route("/api/auth/verify", get(handlers::verify_token))
.route("/api/user/info", get(handlers::get_user_info))
.route("/api/calendar/create", post(handlers::create_calendar))
.route("/api/calendar/delete", post(handlers::delete_calendar))
.route("/api/calendar/events", get(handlers::get_calendar_events))
.route("/api/calendar/events/:uid", get(handlers::refresh_event))
.layer(

View File

@@ -47,6 +47,17 @@ pub struct CreateCalendarResponse {
pub message: String,
}
#[derive(Debug, Deserialize)]
pub struct DeleteCalendarRequest {
pub path: String,
}
#[derive(Debug, Serialize)]
pub struct DeleteCalendarResponse {
pub success: bool,
pub message: String,
}
// Error handling
#[derive(Debug)]
pub enum ApiError {