Editing Series Events via the Modal #5
@@ -201,6 +201,7 @@ pub struct UpdateEventSeriesRequest {
|
||||
// Update scope control
|
||||
pub update_scope: String, // "this_only", "this_and_future", "all_in_series"
|
||||
pub occurrence_date: Option<String>, // ISO date string for specific occurrence being updated
|
||||
pub changed_fields: Option<Vec<String>>, // List of field names that were changed (for optimization)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
||||
@@ -2,7 +2,7 @@ use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use web_sys::MouseEvent;
|
||||
use crate::components::{Sidebar, ViewMode, Theme, CreateCalendarModal, ContextMenu, EventContextMenu, CalendarContextMenu, CreateEventModal, EventCreationData, RouteHandler, EventStatus, EventClass, ReminderType, RecurrenceType, DeleteAction};
|
||||
use crate::components::{Sidebar, ViewMode, Theme, CreateCalendarModal, ContextMenu, EventContextMenu, CalendarContextMenu, CreateEventModal, EventCreationData, RouteHandler, EventStatus, EventClass, ReminderType, RecurrenceType, DeleteAction, EditAction};
|
||||
use crate::services::{CalendarService, calendar_service::UserInfo};
|
||||
use crate::models::ical::VEvent;
|
||||
use chrono::NaiveDate;
|
||||
@@ -54,6 +54,7 @@ pub fn App() -> Html {
|
||||
let calendar_context_menu_date = use_state(|| -> Option<NaiveDate> { None });
|
||||
let create_event_modal_open = use_state(|| false);
|
||||
let selected_date_for_event = use_state(|| -> Option<NaiveDate> { None });
|
||||
let event_edit_scope = use_state(|| -> Option<EditAction> { None });
|
||||
let _recurring_edit_modal_open = use_state(|| false);
|
||||
let _recurring_edit_event = use_state(|| -> Option<VEvent> { None });
|
||||
let _recurring_edit_data = use_state(|| -> Option<EventCreationData> { None });
|
||||
@@ -738,8 +739,10 @@ pub fn App() -> Html {
|
||||
let _event_context_menu_event = event_context_menu_event.clone();
|
||||
let event_context_menu_open = event_context_menu_open.clone();
|
||||
let create_event_modal_open = create_event_modal_open.clone();
|
||||
move |_| {
|
||||
// Close the context menu and open the edit modal
|
||||
let event_edit_scope = event_edit_scope.clone();
|
||||
move |edit_action: EditAction| {
|
||||
// Set the edit scope and close the context menu
|
||||
event_edit_scope.set(Some(edit_action));
|
||||
event_context_menu_open.set(false);
|
||||
create_event_modal_open.set(true);
|
||||
}
|
||||
@@ -840,13 +843,16 @@ pub fn App() -> Html {
|
||||
is_open={*create_event_modal_open}
|
||||
selected_date={(*selected_date_for_event).clone()}
|
||||
event_to_edit={(*event_context_menu_event).clone()}
|
||||
edit_scope={(*event_edit_scope).clone()}
|
||||
on_close={Callback::from({
|
||||
let create_event_modal_open = create_event_modal_open.clone();
|
||||
let event_context_menu_event = event_context_menu_event.clone();
|
||||
let event_edit_scope = event_edit_scope.clone();
|
||||
move |_| {
|
||||
create_event_modal_open.set(false);
|
||||
// Clear the event being edited
|
||||
// Clear the event being edited and edit scope
|
||||
event_context_menu_event.set(None);
|
||||
event_edit_scope.set(None);
|
||||
}
|
||||
})}
|
||||
on_create={on_event_create}
|
||||
@@ -854,10 +860,12 @@ pub fn App() -> Html {
|
||||
let auth_token = auth_token.clone();
|
||||
let create_event_modal_open = create_event_modal_open.clone();
|
||||
let event_context_menu_event = event_context_menu_event.clone();
|
||||
let event_edit_scope = event_edit_scope.clone();
|
||||
move |(original_event, updated_data): (VEvent, EventCreationData)| {
|
||||
web_sys::console::log_1(&format!("Updating event: {:?}", updated_data).into());
|
||||
web_sys::console::log_1(&format!("Updating event: {:?}, edit_scope: {:?}", updated_data, updated_data.edit_scope).into());
|
||||
create_event_modal_open.set(false);
|
||||
event_context_menu_event.set(None);
|
||||
event_edit_scope.set(None);
|
||||
|
||||
if let Some(token) = (*auth_token).clone() {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
|
||||
@@ -4,6 +4,7 @@ use wasm_bindgen::JsCast;
|
||||
use chrono::{NaiveDate, NaiveTime, Local, TimeZone, Utc, Datelike};
|
||||
use crate::services::calendar_service::CalendarInfo;
|
||||
use crate::models::ical::VEvent;
|
||||
use crate::components::EditAction;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct CreateEventModalProps {
|
||||
@@ -18,6 +19,8 @@ pub struct CreateEventModalProps {
|
||||
pub initial_start_time: Option<NaiveTime>,
|
||||
#[prop_or_default]
|
||||
pub initial_end_time: Option<NaiveTime>,
|
||||
#[prop_or_default]
|
||||
pub edit_scope: Option<EditAction>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
@@ -330,6 +333,10 @@ pub struct EventCreationData {
|
||||
pub monthly_by_day: Option<String>, // For monthly: "1MO" = first Monday, "2TU" = second Tuesday, etc.
|
||||
pub monthly_by_monthday: Option<u8>, // For monthly: day of month (1-31)
|
||||
pub yearly_by_month: Vec<bool>, // For yearly: [Jan, Feb, Mar, ..., Dec]
|
||||
|
||||
// Edit scope and tracking fields
|
||||
pub edit_scope: Option<EditAction>,
|
||||
pub changed_fields: Vec<String>, // List of field names that were changed
|
||||
}
|
||||
|
||||
impl Default for EventCreationData {
|
||||
@@ -365,6 +372,10 @@ impl Default for EventCreationData {
|
||||
monthly_by_day: None,
|
||||
monthly_by_monthday: None,
|
||||
yearly_by_month: vec![false; 12], // [Jan, Feb, ..., Dec] - all false by default
|
||||
|
||||
// Edit scope and tracking defaults
|
||||
edit_scope: None,
|
||||
changed_fields: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,6 +577,10 @@ impl EventCreationData {
|
||||
} else {
|
||||
vec![false; 12]
|
||||
},
|
||||
|
||||
// Edit scope and tracking defaults (will be set later if needed)
|
||||
edit_scope: None,
|
||||
changed_fields: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,9 +608,9 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
|
||||
let active_tab = use_state(|| ModalTab::default());
|
||||
|
||||
// Initialize with selected date or event data if provided
|
||||
use_effect_with((props.selected_date, props.event_to_edit.clone(), props.is_open, props.available_calendars.clone(), props.initial_start_time, props.initial_end_time), {
|
||||
use_effect_with((props.selected_date, props.event_to_edit.clone(), props.is_open, props.available_calendars.clone(), props.initial_start_time, props.initial_end_time, props.edit_scope.clone()), {
|
||||
let event_data = event_data.clone();
|
||||
move |(selected_date, event_to_edit, is_open, available_calendars, initial_start_time, initial_end_time)| {
|
||||
move |(selected_date, event_to_edit, is_open, available_calendars, initial_start_time, initial_end_time, edit_scope)| {
|
||||
if *is_open {
|
||||
let mut data = if let Some(event) = event_to_edit {
|
||||
// Pre-populate with event data for editing
|
||||
@@ -625,6 +640,11 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
|
||||
data.selected_calendar = Some(available_calendars[0].path.clone());
|
||||
}
|
||||
|
||||
// Set edit scope if provided
|
||||
if let Some(scope) = edit_scope {
|
||||
data.edit_scope = Some(scope.clone());
|
||||
}
|
||||
|
||||
event_data.set(data);
|
||||
}
|
||||
|| ()
|
||||
@@ -644,12 +664,25 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
|
||||
})
|
||||
};
|
||||
|
||||
// Helper function to track field changes
|
||||
let _track_field_change = |data: &mut EventCreationData, field_name: &str| {
|
||||
if !data.changed_fields.contains(&field_name.to_string()) {
|
||||
data.changed_fields.push(field_name.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
let on_title_input = {
|
||||
let event_data = event_data.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
if let Some(input) = e.target_dyn_into::<HtmlInputElement>() {
|
||||
let mut data = (*event_data).clone();
|
||||
data.title = input.value();
|
||||
let new_value = input.value();
|
||||
if data.title != new_value {
|
||||
data.title = new_value;
|
||||
if !data.changed_fields.contains(&"title".to_string()) {
|
||||
data.changed_fields.push("title".to_string());
|
||||
}
|
||||
}
|
||||
event_data.set(data);
|
||||
}
|
||||
})
|
||||
@@ -661,7 +694,13 @@ pub fn create_event_modal(props: &CreateEventModalProps) -> Html {
|
||||
if let Some(select) = e.target_dyn_into::<HtmlSelectElement>() {
|
||||
let mut data = (*event_data).clone();
|
||||
let value = select.value();
|
||||
data.selected_calendar = if value.is_empty() { None } else { Some(value) };
|
||||
let new_calendar = if value.is_empty() { None } else { Some(value) };
|
||||
if data.selected_calendar != new_calendar {
|
||||
data.selected_calendar = new_calendar;
|
||||
if !data.changed_fields.contains(&"selected_calendar".to_string()) {
|
||||
data.changed_fields.push("selected_calendar".to_string());
|
||||
}
|
||||
}
|
||||
event_data.set(data);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,13 +9,20 @@ pub enum DeleteAction {
|
||||
DeleteSeries,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum EditAction {
|
||||
EditThis,
|
||||
EditFuture,
|
||||
EditAll,
|
||||
}
|
||||
|
||||
#[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_edit: Callback<EditAction>,
|
||||
pub on_delete: Callback<DeleteAction>,
|
||||
pub on_close: Callback<()>,
|
||||
}
|
||||
@@ -38,11 +45,11 @@ pub fn event_context_menu(props: &EventContextMenuProps) -> Html {
|
||||
.map(|event| event.rrule.is_some())
|
||||
.unwrap_or(false);
|
||||
|
||||
let on_edit_click = {
|
||||
let create_edit_callback = |action: EditAction| {
|
||||
let on_edit = props.on_edit.clone();
|
||||
let on_close = props.on_close.clone();
|
||||
Callback::from(move |_: MouseEvent| {
|
||||
on_edit.emit(());
|
||||
on_edit.emit(action.clone());
|
||||
on_close.emit(());
|
||||
})
|
||||
};
|
||||
@@ -62,9 +69,29 @@ pub fn event_context_menu(props: &EventContextMenuProps) -> Html {
|
||||
class="context-menu"
|
||||
style={style}
|
||||
>
|
||||
<div class="context-menu-item" onclick={on_edit_click}>
|
||||
{"Edit Event"}
|
||||
</div>
|
||||
{
|
||||
if is_recurring {
|
||||
html! {
|
||||
<>
|
||||
<div class="context-menu-item" onclick={create_edit_callback(EditAction::EditThis)}>
|
||||
{"Edit This Event"}
|
||||
</div>
|
||||
<div class="context-menu-item" onclick={create_edit_callback(EditAction::EditFuture)}>
|
||||
{"Edit This and Future Events"}
|
||||
</div>
|
||||
<div class="context-menu-item" onclick={create_edit_callback(EditAction::EditAll)}>
|
||||
{"Edit All Events in Series"}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<div class="context-menu-item" onclick={create_edit_callback(EditAction::EditThis)}>
|
||||
{"Edit Event"}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
if is_recurring {
|
||||
html! {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use week_view::WeekView;
|
||||
pub use event_modal::EventModal;
|
||||
pub use create_calendar_modal::CreateCalendarModal;
|
||||
pub use context_menu::ContextMenu;
|
||||
pub use event_context_menu::{EventContextMenu, DeleteAction};
|
||||
pub use event_context_menu::{EventContextMenu, DeleteAction, EditAction};
|
||||
pub use calendar_context_menu::CalendarContextMenu;
|
||||
pub use create_event_modal::{CreateEventModal, EventCreationData, EventStatus, EventClass, ReminderType, RecurrenceType};
|
||||
pub use sidebar::{Sidebar, ViewMode, Theme};
|
||||
|
||||
Reference in New Issue
Block a user