From 948d2398671d97b2eda80ac4922c0cba02a8d583 Mon Sep 17 00:00:00 2001 From: mARTin Date: Tue, 24 Mar 2026 16:40:33 +0100 Subject: [PATCH] feat: redesign recurring edit dialogs with Google Calendar style and i18n - Redesigned drag/resize and modal recurring edit dialogs with radio buttons (This event / This and following / All events) with descriptions - Added German/English i18n to both recurring edit dialogs - Repeat-on day buttons now respect weekStartDay setting (Mon/Sun start) - Added language prop to CalendarEventModal v1.70.0 Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- src/components/CalendarEventModal.tsx | 173 ++++++++++++++++---------- src/components/WeeklyView.tsx | 103 +++++++-------- 3 files changed, 162 insertions(+), 116 deletions(-) diff --git a/package.json b/package.json index 07f7ce6..fb41a91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.69.0", + "version": "1.70.0", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/src/components/CalendarEventModal.tsx b/src/components/CalendarEventModal.tsx index e484dfc..9c044b0 100644 --- a/src/components/CalendarEventModal.tsx +++ b/src/components/CalendarEventModal.tsx @@ -40,6 +40,8 @@ interface CalendarEventModalProps { initialDate?: Date; initialStartTime?: string; connections: any[]; + weekStartDay?: number; // 0=Sunday, 1=Monday + language?: string; onClose: () => void; onSave: (eventData: any) => Promise; onDelete?: (eventId: string, calendarId: string, deleteMode?: string) => Promise; @@ -50,6 +52,8 @@ export default function CalendarEventModal({ initialDate, initialStartTime, connections, + weekStartDay = 0, + language = 'en', onClose, onSave, onDelete @@ -223,6 +227,7 @@ export default function CalendarEventModal({ const [isDeleteConfirming, setIsDeleteConfirming] = useState(false); const [showRecurringDeleteOptions, setShowRecurringDeleteOptions] = useState(false); const [showRecurringEditOptions, setShowRecurringEditOptions] = useState(false); + const [recurringEditMode, setRecurringEditMode] = useState<'this' | 'future' | 'all'>('this'); const handleDelete = async (mode?: string) => { if (!event?.id || !onDelete) return; @@ -503,26 +508,30 @@ export default function CalendarEventModal({
Repeat on
- {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, i) => ( - - ))} + {(() => { + const allDays = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; + const dayIndices = Array.from({ length: 7 }, (_, i) => (i + weekStartDay) % 7); + return dayIndices.map(dow => ( + + )); + })()}
)} @@ -848,55 +857,87 @@ export default function CalendarEventModal({ Cancel )} - {!showRecurringEditOptions ? ( - - ) : ( -
- Save: - {[ - { mode: 'this', label: 'This event' }, - { mode: 'all', label: 'All events' }, - ].map(({ mode, label }) => ( - - ))} - -
- )} + + {/* Recurring edit mode overlay */} + {showRecurringEditOptions && ( +
setShowRecurringEditOptions(false)}> +
e.stopPropagation()}> +
+ {language === 'de' ? 'Wiederkehrendes Ereignis bearbeiten' : 'Edit recurring event'} + +
+
+ {([ + { value: 'this' as const, title: language === 'de' ? 'Nur dieses Ereignis' : 'This event', desc: language === 'de' ? 'Alle anderen Ereignisse der Serie bleiben unverändert.' : 'All other events in the series stay the same.' }, + { value: 'future' as const, title: language === 'de' ? 'Dieses und folgende Ereignisse' : 'This and following events', desc: language === 'de' ? 'Dieses und alle zukünftigen Ereignisse der Serie werden geändert.' : 'This and all future events in the series will be changed.' }, + { value: 'all' as const, title: language === 'de' ? 'Alle Ereignisse' : 'All events', desc: language === 'de' ? 'Alle Ereignisse der Serie werden geändert.' : 'All events in the series will be changed.' }, + ]).map(opt => ( + + ))} +
+
+ + +
+
+
+ )} ); } diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index d4d2eac..210ba58 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -2247,6 +2247,7 @@ export default function WeeklyView() { originalStartTime: string; originalEndTime: string; } | null>(null); + const [recurringDragEditMode, setRecurringDragEditMode] = useState<'this' | 'future' | 'all'>('this'); // Dark Mode Persistence & Class Toggle const [mounted, setMounted] = useState(false); @@ -3820,6 +3821,7 @@ export default function WeeklyView() { // Check if this is a recurring event — if so, ask before saving const draggedEvent = calendarEvents.find(e => e.id === eventDragState.eventId); if (draggedEvent?.isRecurring) { + setRecurringDragEditMode('this'); setPendingRecurringDrag({ eventId: eventDragState.eventId, calendarId: eventDragState.calendarId || '', @@ -3870,7 +3872,7 @@ export default function WeeklyView() { }, [eventDragState, effectiveCellDuration, calendarEvents]); // Handle recurring event drag confirm (this/all) - const handleRecurringDragConfirm = async (editMode: 'this' | 'all') => { + const handleRecurringDragConfirm = async (editMode: 'this' | 'future' | 'all') => { if (!pendingRecurringDrag) return; const { eventId, calendarId, recurringEventId, startTime, endTime, originalStartTime, originalEndTime } = pendingRecurringDrag; try { @@ -9153,6 +9155,8 @@ export default function WeeklyView() { initialDate={calendarEventModal.initialDate} initialStartTime={calendarEventModal.initialStartTime} connections={connections} + weekStartDay={weekStartDay} + language={language} onClose={() => setCalendarEventModal({ ...calendarEventModal, isOpen: false }) } @@ -9166,57 +9170,58 @@ export default function WeeklyView() {
-
e.stopPropagation()} - > -
+ background: 'rgba(0,0,0,0.35)', backdropFilter: 'blur(2px)', + }} onClick={handleRecurringDragCancel}> +
e.stopPropagation()}> +
{language === 'de' ? 'Wiederkehrendes Ereignis bearbeiten' : 'Edit recurring event'}
-
- {language === 'de' ? 'Änderung anwenden auf:' : 'Apply change to:'} +
+ {([ + { value: 'this' as const, title: language === 'de' ? 'Nur dieses Ereignis' : 'This event', desc: language === 'de' ? 'Alle anderen Ereignisse der Serie bleiben unverändert.' : 'All other events in the series stay the same.' }, + { value: 'future' as const, title: language === 'de' ? 'Dieses und folgende Ereignisse' : 'This and following events', desc: language === 'de' ? 'Dieses und alle zukünftigen Ereignisse der Serie werden geändert.' : 'This and all future events in the series will be changed.' }, + { value: 'all' as const, title: language === 'de' ? 'Alle Ereignisse' : 'All events', desc: language === 'de' ? 'Alle Ereignisse der Serie werden geändert.' : 'All events in the series will be changed.' }, + ]).map(opt => ( + + ))}
-
- - - +
+ +