feat(events): reliable holiday skipping for recurrences + UI badge; clean logs
Backend: generate EventException on create/update when skip_holidays or recurrence changes; emit RecurrenceException (EXDATE) with exact occurrence start time (UTC) API: return master events with RecurrenceRule + RecurrenceException Frontend: map RecurrenceException → recurrenceException; ensure SkipHolidays instances never render on holidays; place TentTree icon (black) next to main event icon via template Docs: update README and Copilot instructions for recurrence/holiday behavior Cleanup: remove dataSource and debug console logs
This commit is contained in:
@@ -6,6 +6,7 @@ import { DropDownListComponent, MultiSelectComponent } from '@syncfusion/ej2-rea
|
||||
import { CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
|
||||
import CustomSelectUploadEventModal from './CustomSelectUploadEventModal';
|
||||
import { updateEvent } from '../apiEvents';
|
||||
// Holiday exceptions are now created in the backend
|
||||
|
||||
type CustomEventData = {
|
||||
title: string;
|
||||
@@ -76,7 +77,10 @@ const CustomEventModal: React.FC<CustomEventModalProps> = ({
|
||||
const [repeat, setRepeat] = React.useState(initialData.repeat || false);
|
||||
const [weekdays, setWeekdays] = React.useState<number[]>(initialData.weekdays || []);
|
||||
const [repeatUntil, setRepeatUntil] = React.useState(initialData.repeatUntil || null);
|
||||
const [skipHolidays, setSkipHolidays] = React.useState(initialData.skipHolidays || false);
|
||||
// Default to true so recurrences skip holidays by default
|
||||
const [skipHolidays, setSkipHolidays] = React.useState(
|
||||
initialData.skipHolidays !== undefined ? initialData.skipHolidays : true
|
||||
);
|
||||
const [errors, setErrors] = React.useState<{ [key: string]: string }>({});
|
||||
// --- KORREKTUR: Media, SlideshowInterval, WebsiteUrl aus initialData übernehmen ---
|
||||
const [media, setMedia] = React.useState<{ id: string; path: string; name: string } | null>(
|
||||
@@ -104,7 +108,7 @@ const CustomEventModal: React.FC<CustomEventModalProps> = ({
|
||||
setRepeat(initialData.repeat || false);
|
||||
setWeekdays(initialData.weekdays || []);
|
||||
setRepeatUntil(initialData.repeatUntil || null);
|
||||
setSkipHolidays(initialData.skipHolidays || false);
|
||||
setSkipHolidays(initialData.skipHolidays !== undefined ? initialData.skipHolidays : true);
|
||||
// --- KORREKTUR: Media, SlideshowInterval, WebsiteUrl aus initialData übernehmen ---
|
||||
setMedia(initialData.media ?? null);
|
||||
setSlideshowInterval(initialData.slideshowInterval ?? 10);
|
||||
@@ -190,6 +194,27 @@ const CustomEventModal: React.FC<CustomEventModalProps> = ({
|
||||
|
||||
const group_id = typeof groupName === 'object' && groupName !== null ? groupName.id : groupName;
|
||||
|
||||
// Build recurrence rule if repeat is enabled
|
||||
let recurrenceRule = null;
|
||||
let recurrenceEnd = null;
|
||||
if (repeat && weekdays.length > 0) {
|
||||
// Convert weekdays to RRULE format (0=Monday -> MO)
|
||||
const rruleDays = weekdays.map(day => {
|
||||
const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'];
|
||||
return dayNames[day];
|
||||
}).join(',');
|
||||
|
||||
recurrenceRule = `FREQ=WEEKLY;BYDAY=${rruleDays}`;
|
||||
if (repeatUntil) {
|
||||
const untilDate = new Date(repeatUntil);
|
||||
untilDate.setHours(23, 59, 59);
|
||||
recurrenceEnd = untilDate.toISOString();
|
||||
// Note: RRULE UNTIL should be in UTC format for consistency
|
||||
const untilUTC = untilDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
|
||||
recurrenceRule += `;UNTIL=${untilUTC}`;
|
||||
}
|
||||
}
|
||||
|
||||
const payload: CustomEventData & { [key: string]: unknown } = {
|
||||
group_id,
|
||||
title,
|
||||
@@ -225,6 +250,8 @@ const CustomEventModal: React.FC<CustomEventModalProps> = ({
|
||||
event_type: type,
|
||||
is_active: 1,
|
||||
created_by: 1,
|
||||
recurrence_rule: recurrenceRule,
|
||||
recurrence_end: recurrenceEnd,
|
||||
};
|
||||
|
||||
if (type === 'presentation') {
|
||||
@@ -250,6 +277,7 @@ const CustomEventModal: React.FC<CustomEventModalProps> = ({
|
||||
});
|
||||
res = await res.json();
|
||||
}
|
||||
|
||||
if (res.success) {
|
||||
onSave(payload);
|
||||
onClose(); // <--- Box nach erfolgreichem Speichern schließen
|
||||
|
||||
Reference in New Issue
Block a user