Some checks failed
Build and Push Docker Image / docker (push) Failing after 1m7s
Moved event fetching logic from CalendarView to Calendar component to properly use the visible date range instead of hardcoded current month. The Calendar component already tracks the current visible date through navigation, so events now load correctly for August and other months when navigating. Changes: - Calendar component now manages its own events state and fetching - Event fetching responds to current_date changes from navigation - CalendarView simplified to just render Calendar component - Fixed cargo fmt/clippy formatting across codebase 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
239 lines
9.3 KiB
Rust
239 lines
9.3 KiB
Rust
use crate::models::ical::VEvent;
|
||
use chrono::{DateTime, Utc};
|
||
use yew::prelude::*;
|
||
|
||
#[derive(Properties, PartialEq)]
|
||
pub struct EventModalProps {
|
||
pub event: Option<VEvent>,
|
||
pub on_close: Callback<()>,
|
||
}
|
||
|
||
#[function_component]
|
||
pub fn EventModal(props: &EventModalProps) -> Html {
|
||
let close_modal = {
|
||
let on_close = props.on_close.clone();
|
||
Callback::from(move |_| {
|
||
on_close.emit(());
|
||
})
|
||
};
|
||
|
||
let backdrop_click = {
|
||
let on_close = props.on_close.clone();
|
||
Callback::from(move |e: MouseEvent| {
|
||
if e.target() == e.current_target() {
|
||
on_close.emit(());
|
||
}
|
||
})
|
||
};
|
||
|
||
if let Some(ref event) = props.event {
|
||
html! {
|
||
<div class="modal-backdrop" onclick={backdrop_click}>
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>{"Event Details"}</h3>
|
||
<button class="modal-close" onclick={close_modal}>{"×"}</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="event-detail">
|
||
<strong>{"Title:"}</strong>
|
||
<span>{event.get_title()}</span>
|
||
</div>
|
||
|
||
{
|
||
if let Some(ref description) = event.description {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Description:"}</strong>
|
||
<span>{description}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
<div class="event-detail">
|
||
<strong>{"Start:"}</strong>
|
||
<span>{format_datetime(&event.dtstart, event.all_day)}</span>
|
||
</div>
|
||
|
||
{
|
||
if let Some(ref end) = event.dtend {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"End:"}</strong>
|
||
<span>{format_datetime(end, event.all_day)}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
<div class="event-detail">
|
||
<strong>{"All Day:"}</strong>
|
||
<span>{if event.all_day { "Yes" } else { "No" }}</span>
|
||
</div>
|
||
|
||
{
|
||
if let Some(ref location) = event.location {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Location:"}</strong>
|
||
<span>{location}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
<div class="event-detail">
|
||
<strong>{"Status:"}</strong>
|
||
<span>{event.get_status_display()}</span>
|
||
</div>
|
||
|
||
<div class="event-detail">
|
||
<strong>{"Privacy:"}</strong>
|
||
<span>{event.get_class_display()}</span>
|
||
</div>
|
||
|
||
<div class="event-detail">
|
||
<strong>{"Priority:"}</strong>
|
||
<span>{event.get_priority_display()}</span>
|
||
</div>
|
||
|
||
{
|
||
if let Some(ref organizer) = event.organizer {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Organizer:"}</strong>
|
||
<span>{organizer.cal_address.clone()}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
{
|
||
if !event.attendees.is_empty() {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Attendees:"}</strong>
|
||
<span>{event.attendees.iter().map(|a| a.cal_address.clone()).collect::<Vec<_>>().join(", ")}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
{
|
||
if !event.categories.is_empty() {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Categories:"}</strong>
|
||
<span>{event.categories.join(", ")}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
{
|
||
if let Some(ref recurrence) = event.rrule {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Repeats:"}</strong>
|
||
<span>{format_recurrence_rule(recurrence)}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Repeats:"}</strong>
|
||
<span>{"No"}</span>
|
||
</div>
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
if !event.alarms.is_empty() {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Reminders:"}</strong>
|
||
<span>{"Alarms configured"}</span> /* TODO: Convert VAlarm to displayable format */
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Reminders:"}</strong>
|
||
<span>{"None"}</span>
|
||
</div>
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
if let Some(ref created) = event.created {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Created:"}</strong>
|
||
<span>{format_datetime(created, false)}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
{
|
||
if let Some(ref modified) = event.last_modified {
|
||
html! {
|
||
<div class="event-detail">
|
||
<strong>{"Last Modified:"}</strong>
|
||
<span>{format_datetime(modified, false)}</span>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
}
|
||
} else {
|
||
html! {}
|
||
}
|
||
}
|
||
|
||
fn format_datetime(dt: &DateTime<Utc>, all_day: bool) -> String {
|
||
if all_day {
|
||
dt.format("%B %d, %Y").to_string()
|
||
} else {
|
||
dt.format("%B %d, %Y at %I:%M %p").to_string()
|
||
}
|
||
}
|
||
|
||
fn format_recurrence_rule(rrule: &str) -> String {
|
||
// Basic parsing of RRULE to display user-friendly text
|
||
if rrule.contains("FREQ=DAILY") {
|
||
"Daily".to_string()
|
||
} else if rrule.contains("FREQ=WEEKLY") {
|
||
"Weekly".to_string()
|
||
} else if rrule.contains("FREQ=MONTHLY") {
|
||
"Monthly".to_string()
|
||
} else if rrule.contains("FREQ=YEARLY") {
|
||
"Yearly".to_string()
|
||
} else {
|
||
// Show the raw rule if we can't parse it
|
||
format!("Custom ({})", rrule)
|
||
}
|
||
}
|