Implement event deletion with right-click context menu

- Add EventContextMenu component with delete option
- Create DELETE /api/calendar/events/delete endpoint
- Implement CalDAV event deletion in backend
- Add proper URL construction for CalDAV event hrefs
- Integrate context menu with calendar event right-clicks
- Auto-refresh UI after successful event deletion

🤖 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 22:07:09 -04:00
parent b444ae710d
commit 7e62e3b7e3
10 changed files with 313 additions and 8 deletions

View File

@@ -1,7 +1,7 @@
use yew::prelude::*;
use yew_router::prelude::*;
use crate::components::Login;
use crate::services::calendar_service::UserInfo;
use crate::services::calendar_service::{UserInfo, CalendarEvent};
#[derive(Clone, Routable, PartialEq)]
pub enum Route {
@@ -18,6 +18,8 @@ pub struct RouteHandlerProps {
pub auth_token: Option<String>,
pub user_info: Option<UserInfo>,
pub on_login: Callback<String>,
#[prop_or_default]
pub on_event_context_menu: Option<Callback<(web_sys::MouseEvent, CalendarEvent)>>,
}
#[function_component(RouteHandler)]
@@ -25,12 +27,14 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html {
let auth_token = props.auth_token.clone();
let user_info = props.user_info.clone();
let on_login = props.on_login.clone();
let on_event_context_menu = props.on_event_context_menu.clone();
html! {
<Switch<Route> render={move |route| {
let auth_token = auth_token.clone();
let user_info = user_info.clone();
let on_login = on_login.clone();
let on_event_context_menu = on_event_context_menu.clone();
match route {
Route::Home => {
@@ -49,7 +53,12 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html {
}
Route::Calendar => {
if auth_token.is_some() {
html! { <CalendarView user_info={user_info} /> }
html! {
<CalendarView
user_info={user_info}
on_event_context_menu={on_event_context_menu}
/>
}
} else {
html! { <Redirect<Route> to={Route::Login}/> }
}
@@ -62,10 +71,12 @@ pub fn route_handler(props: &RouteHandlerProps) -> Html {
#[derive(Properties, PartialEq)]
pub struct CalendarViewProps {
pub user_info: Option<UserInfo>,
#[prop_or_default]
pub on_event_context_menu: Option<Callback<(web_sys::MouseEvent, CalendarEvent)>>,
}
use gloo_storage::{LocalStorage, Storage};
use crate::services::{CalendarService, CalendarEvent};
use crate::services::CalendarService;
use crate::components::Calendar;
use std::collections::HashMap;
use chrono::{Local, NaiveDate, Datelike};
@@ -79,6 +90,7 @@ pub fn calendar_view(props: &CalendarViewProps) -> Html {
let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
let today = Local::now().date_naive();
let current_year = today.year();
let current_month = today.month();
@@ -212,12 +224,24 @@ pub fn calendar_view(props: &CalendarViewProps) -> Html {
html! {
<div class="calendar-error">
<p>{format!("Error: {}", err)}</p>
<Calendar events={HashMap::new()} on_event_click={dummy_callback} refreshing_event_uid={(*refreshing_event).clone()} user_info={props.user_info.clone()} />
<Calendar
events={HashMap::new()}
on_event_click={dummy_callback}
refreshing_event_uid={(*refreshing_event).clone()}
user_info={props.user_info.clone()}
on_event_context_menu={props.on_event_context_menu.clone()}
/>
</div>
}
} else {
html! {
<Calendar events={(*events).clone()} on_event_click={on_event_click} refreshing_event_uid={(*refreshing_event).clone()} user_info={props.user_info.clone()} />
<Calendar
events={(*events).clone()}
on_event_click={on_event_click}
refreshing_event_uid={(*refreshing_event).clone()}
user_info={props.user_info.clone()}
on_event_context_menu={props.on_event_context_menu.clone()}
/>
}
}
}