- Created dedicated EventModal component in src/components/event_modal.rs - Extracted modal logic and styling from calendar component for better separation - Updated data flow to pass full CalendarEvent objects instead of strings - Added PartialEq derive to CalendarEvent for component props - Updated service layer to group events by CalendarEvent objects - Enhanced event click handling to show detailed event information - Modal displays title, description, location, start/end times, and status - Maintained existing modal styling and user interaction patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
107 lines
3.4 KiB
Rust
107 lines
3.4 KiB
Rust
use chrono::{DateTime, Utc, NaiveDate};
|
|
use serde::{Deserialize, Serialize};
|
|
use wasm_bindgen::JsCast;
|
|
use wasm_bindgen_futures::JsFuture;
|
|
use web_sys::{Request, RequestInit, RequestMode, Response};
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
pub struct CalendarEvent {
|
|
pub uid: String,
|
|
pub summary: Option<String>,
|
|
pub description: Option<String>,
|
|
pub start: DateTime<Utc>,
|
|
pub end: Option<DateTime<Utc>>,
|
|
pub location: Option<String>,
|
|
pub status: String,
|
|
pub all_day: bool,
|
|
}
|
|
|
|
impl CalendarEvent {
|
|
/// Get the date for this event (for calendar display)
|
|
pub fn get_date(&self) -> NaiveDate {
|
|
if self.all_day {
|
|
self.start.date_naive()
|
|
} else {
|
|
self.start.date_naive()
|
|
}
|
|
}
|
|
|
|
/// Get display title for the event
|
|
pub fn get_title(&self) -> String {
|
|
self.summary.clone().unwrap_or_else(|| "Untitled Event".to_string())
|
|
}
|
|
}
|
|
|
|
pub struct CalendarService {
|
|
base_url: String,
|
|
}
|
|
|
|
impl CalendarService {
|
|
pub fn new() -> Self {
|
|
let base_url = option_env!("BACKEND_API_URL")
|
|
.unwrap_or("http://localhost:3000/api")
|
|
.to_string();
|
|
|
|
Self { base_url }
|
|
}
|
|
|
|
/// Fetch calendar events for a specific month
|
|
pub async fn fetch_events_for_month(
|
|
&self,
|
|
token: &str,
|
|
year: i32,
|
|
month: u32
|
|
) -> Result<Vec<CalendarEvent>, String> {
|
|
let window = web_sys::window().ok_or("No global window exists")?;
|
|
|
|
let opts = RequestInit::new();
|
|
opts.set_method("GET");
|
|
opts.set_mode(RequestMode::Cors);
|
|
|
|
let url = format!("{}/calendar/events?year={}&month={}", self.base_url, year, month);
|
|
let request = Request::new_with_str_and_init(&url, &opts)
|
|
.map_err(|e| format!("Request creation failed: {:?}", e))?;
|
|
|
|
request.headers().set("Authorization", &format!("Bearer {}", token))
|
|
.map_err(|e| format!("Authorization header setting failed: {:?}", e))?;
|
|
|
|
let resp_value = JsFuture::from(window.fetch_with_request(&request))
|
|
.await
|
|
.map_err(|e| format!("Network request failed: {:?}", e))?;
|
|
|
|
let resp: Response = resp_value.dyn_into()
|
|
.map_err(|e| format!("Response cast failed: {:?}", e))?;
|
|
|
|
let text = JsFuture::from(resp.text()
|
|
.map_err(|e| format!("Text extraction failed: {:?}", e))?)
|
|
.await
|
|
.map_err(|e| format!("Text promise failed: {:?}", e))?;
|
|
|
|
let text_string = text.as_string()
|
|
.ok_or("Response text is not a string")?;
|
|
|
|
if resp.ok() {
|
|
let events: Vec<CalendarEvent> = serde_json::from_str(&text_string)
|
|
.map_err(|e| format!("JSON parsing failed: {}", e))?;
|
|
Ok(events)
|
|
} else {
|
|
Err(format!("Request failed with status {}: {}", resp.status(), text_string))
|
|
}
|
|
}
|
|
|
|
/// Convert events to a HashMap grouped by date for calendar display
|
|
pub fn group_events_by_date(events: Vec<CalendarEvent>) -> HashMap<NaiveDate, Vec<CalendarEvent>> {
|
|
let mut grouped = HashMap::new();
|
|
|
|
for event in events {
|
|
let date = event.get_date();
|
|
|
|
grouped.entry(date)
|
|
.or_insert_with(Vec::new)
|
|
.push(event);
|
|
}
|
|
|
|
grouped
|
|
}
|
|
} |