Frontend Changes: - Add edit context menu option to EventContextMenu with pencil icon - Enhance CreateEventModal to support both create and edit modes - Add event data conversion methods for pre-populating edit forms - Implement conditional submit logic (on_create vs on_update callbacks) - Add update_event method to CalendarService with POST /calendar/events/update Backend Changes: - Add UpdateEventRequest and UpdateEventResponse models - Implement update_event handler with event search by UID across calendars - Add POST /api/calendar/events/update route - Full validation and parsing of all event properties for updates - Integrate with existing CalDAV client update_event functionality Users can now right-click events, select "Edit Event", modify properties in the modal, and successfully update existing events instead of creating duplicates. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
use yew::prelude::*;
|
|
use web_sys::MouseEvent;
|
|
use crate::services::calendar_service::CalendarEvent;
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
pub enum DeleteAction {
|
|
DeleteThis,
|
|
DeleteFollowing,
|
|
DeleteSeries,
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct EventContextMenuProps {
|
|
pub is_open: bool,
|
|
pub x: i32,
|
|
pub y: i32,
|
|
pub event: Option<CalendarEvent>,
|
|
pub on_edit: Callback<()>,
|
|
pub on_delete: Callback<DeleteAction>,
|
|
pub on_close: Callback<()>,
|
|
}
|
|
|
|
#[function_component(EventContextMenu)]
|
|
pub fn event_context_menu(props: &EventContextMenuProps) -> Html {
|
|
let menu_ref = use_node_ref();
|
|
|
|
if !props.is_open {
|
|
return html! {};
|
|
}
|
|
|
|
let style = format!(
|
|
"position: fixed; left: {}px; top: {}px; z-index: 1001;",
|
|
props.x, props.y
|
|
);
|
|
|
|
// Check if the event is recurring
|
|
let is_recurring = props.event.as_ref()
|
|
.map(|event| event.recurrence_rule.is_some())
|
|
.unwrap_or(false);
|
|
|
|
let on_edit_click = {
|
|
let on_edit = props.on_edit.clone();
|
|
let on_close = props.on_close.clone();
|
|
Callback::from(move |_: MouseEvent| {
|
|
on_edit.emit(());
|
|
on_close.emit(());
|
|
})
|
|
};
|
|
|
|
let create_delete_callback = |action: DeleteAction| {
|
|
let on_delete = props.on_delete.clone();
|
|
let on_close = props.on_close.clone();
|
|
Callback::from(move |_: MouseEvent| {
|
|
on_delete.emit(action.clone());
|
|
on_close.emit(());
|
|
})
|
|
};
|
|
|
|
html! {
|
|
<div
|
|
ref={menu_ref}
|
|
class="context-menu"
|
|
style={style}
|
|
>
|
|
<div class="context-menu-item" onclick={on_edit_click}>
|
|
<span class="context-menu-icon">{"✏️"}</span>
|
|
{"Edit Event"}
|
|
</div>
|
|
{
|
|
if is_recurring {
|
|
html! {
|
|
<>
|
|
<div class="context-menu-item context-menu-delete" onclick={create_delete_callback(DeleteAction::DeleteThis)}>
|
|
<span class="context-menu-icon">{"🗑️"}</span>
|
|
{"Delete This Event"}
|
|
</div>
|
|
<div class="context-menu-item context-menu-delete" onclick={create_delete_callback(DeleteAction::DeleteFollowing)}>
|
|
<span class="context-menu-icon">{"🗑️"}</span>
|
|
{"Delete Following Events"}
|
|
</div>
|
|
<div class="context-menu-item context-menu-delete" onclick={create_delete_callback(DeleteAction::DeleteSeries)}>
|
|
<span class="context-menu-icon">{"🗑️"}</span>
|
|
{"Delete Entire Series"}
|
|
</div>
|
|
</>
|
|
}
|
|
} else {
|
|
html! {
|
|
<div class="context-menu-item context-menu-delete" onclick={create_delete_callback(DeleteAction::DeleteThis)}>
|
|
<span class="context-menu-icon">{"🗑️"}</span>
|
|
{"Delete Event"}
|
|
</div>
|
|
}
|
|
}
|
|
}
|
|
</div>
|
|
}
|
|
} |