Implement shared RFC 5545 VEvent library with workspace restructuring

- Created calendar-models/ shared library with RFC 5545-compliant VEvent structures
- Migrated backend to use shared VEvent with proper field mappings (dtstart/dtend, rrule, exdate, etc.)
- Converted CalDAV client to parse into VEvent structures with structured types
- Updated all CRUD handlers to use VEvent with CalendarUser, Attendee, VAlarm types
- Restructured project as Cargo workspace with frontend/, backend/, calendar-models/
- Updated Trunk configuration for new directory structure
- Fixed all compilation errors and field references throughout codebase
- Updated documentation and build instructions for workspace structure

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-30 11:45:58 -04:00
parent 6887e0b389
commit 15f2d0c6d9
43 changed files with 1962 additions and 945 deletions

View File

@@ -0,0 +1,98 @@
use yew::prelude::*;
use web_sys::MouseEvent;
use crate::models::ical::VEvent;
#[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<VEvent>,
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.rrule.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>
}
}