Add comprehensive iCal properties support to event creation modal

Enhanced the create event modal to include all major iCalendar properties:
- Event status (confirmed/tentative/cancelled)
- Privacy classification (public/private/confidential)
- Priority levels (0-9 numeric scale)
- Organizer email field
- Attendees list (comma-separated emails)
- Categories (comma-separated tags)
- Reminder options (none to 1 week before)
- Recurrence patterns (none/daily/weekly/monthly/yearly)

Updated backend to parse and handle all new fields, with proper enum conversion
and comma-separated list parsing. Events now generate complete iCal data with
STATUS, CLASS, PRIORITY, ORGANIZER, ATTENDEE, CATEGORIES, VALARM, and RRULE properties.

🤖 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:43:03 -04:00
parent 749ffaff58
commit 34461640af
7 changed files with 453 additions and 12 deletions

View File

@@ -415,6 +415,63 @@ pub async fn create_event(
// Generate a unique UID for the event
let uid = format!("{}-{}", uuid::Uuid::new_v4(), chrono::Utc::now().timestamp());
// Parse status
let status = match request.status.to_lowercase().as_str() {
"tentative" => crate::calendar::EventStatus::Tentative,
"cancelled" => crate::calendar::EventStatus::Cancelled,
_ => crate::calendar::EventStatus::Confirmed,
};
// Parse class
let class = match request.class.to_lowercase().as_str() {
"private" => crate::calendar::EventClass::Private,
"confidential" => crate::calendar::EventClass::Confidential,
_ => crate::calendar::EventClass::Public,
};
// Parse attendees (comma-separated email list)
let attendees: Vec<String> = if request.attendees.trim().is_empty() {
Vec::new()
} else {
request.attendees
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
};
// Parse categories (comma-separated list)
let categories: Vec<String> = if request.categories.trim().is_empty() {
Vec::new()
} else {
request.categories
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
};
// Parse reminders (for now, just store as a simple reminder duration)
let reminders: Vec<chrono::Duration> = match request.reminder.to_lowercase().as_str() {
"15min" => vec![chrono::Duration::minutes(15)],
"30min" => vec![chrono::Duration::minutes(30)],
"1hour" => vec![chrono::Duration::hours(1)],
"2hours" => vec![chrono::Duration::hours(2)],
"1day" => vec![chrono::Duration::days(1)],
"2days" => vec![chrono::Duration::days(2)],
"1week" => vec![chrono::Duration::weeks(1)],
_ => Vec::new(),
};
// Parse recurrence (basic implementation)
let recurrence_rule = match request.recurrence.to_lowercase().as_str() {
"daily" => Some("FREQ=DAILY".to_string()),
"weekly" => Some("FREQ=WEEKLY".to_string()),
"monthly" => Some("FREQ=MONTHLY".to_string()),
"yearly" => Some("FREQ=YEARLY".to_string()),
_ => None,
};
// Create the CalendarEvent struct
let event = crate::calendar::CalendarEvent {
uid,
@@ -431,17 +488,21 @@ pub async fn create_event(
} else {
Some(request.location.clone())
},
status: crate::calendar::EventStatus::Confirmed,
class: crate::calendar::EventClass::Public,
priority: None,
organizer: None,
attendees: Vec::new(),
categories: Vec::new(),
status,
class,
priority: request.priority,
organizer: if request.organizer.trim().is_empty() {
None
} else {
Some(request.organizer.clone())
},
attendees,
categories,
created: Some(chrono::Utc::now()),
last_modified: Some(chrono::Utc::now()),
recurrence_rule: None,
recurrence_rule,
all_day: request.all_day,
reminders: Vec::new(),
reminders,
etag: None,
href: None,
calendar_path: Some(calendar_path.clone()),

View File

@@ -80,6 +80,14 @@ pub struct CreateEventRequest {
pub end_time: String, // HH:MM format
pub location: String,
pub all_day: bool,
pub status: String, // confirmed, tentative, cancelled
pub class: String, // public, private, confidential
pub priority: Option<u8>, // 0-9 priority level
pub organizer: String, // organizer email
pub attendees: String, // comma-separated attendee emails
pub categories: String, // comma-separated categories
pub reminder: String, // reminder type
pub recurrence: String, // recurrence type
pub calendar_path: Option<String>, // Optional - use first calendar if not specified
}