feat: add calendar drag-and-drop rescheduling and iCalendar import

- Drag events in week/day views to reschedule (15-min snap intervals)
- Drag events in month view to change date (preserves time)
- Visual snap indicators with time labels during drag
- iCalendar (.ics) file import via JMAP CalendarEvent/parse
- Import modal with file upload, event preview, calendar selector
- File validation (5MB max), bulk import with progress tracking
This commit is contained in:
Matthieu MALVACHE
2026-02-16 21:09:13 +01:00
committed by Matthieu MALVACHE
parent e6f67c24cb
commit ff65693e26
19 changed files with 842 additions and 29 deletions
+19
View File
@@ -25,6 +25,7 @@ interface CalendarStore {
createEvent: (client: JMAPClient, event: Partial<CalendarEvent>) => Promise<CalendarEvent | null>;
updateEvent: (client: JMAPClient, id: string, updates: Partial<CalendarEvent>) => Promise<void>;
deleteEvent: (client: JMAPClient, id: string) => Promise<void>;
importEvents: (client: JMAPClient, events: Partial<CalendarEvent>[], calendarId: string) => Promise<number>;
setSelectedDate: (date: Date) => void;
setViewMode: (mode: CalendarViewMode) => void;
toggleCalendarVisibility: (calendarId: string) => void;
@@ -114,6 +115,24 @@ export const useCalendarStore = create<CalendarStore>()(
}
},
importEvents: async (client, events, calendarId) => {
let imported = 0;
for (const event of events) {
try {
const data: Partial<CalendarEvent> = {
...event,
calendarIds: { [calendarId]: true },
};
const created = await client.createCalendarEvent(data);
set((state) => ({ events: [...state.events, created] }));
imported++;
} catch (error) {
debug.error('Failed to import event:', event.title, error);
}
}
return imported;
},
deleteEvent: async (client, id) => {
set({ error: null });
try {