Agenda refactor

This commit is contained in:
alma 2026-01-15 14:37:09 +01:00
parent 6fefc74fd5
commit f9e8409ab8
3 changed files with 177 additions and 2 deletions

View File

@ -63,7 +63,8 @@ export default async function CalendarPage() {
{ name: { in: ["Privée", "Default"] } },
{
syncConfig: {
isNot: null
isNot: null,
syncEnabled: true // Also check that sync is enabled
}
}
]
@ -140,6 +141,19 @@ export default async function CalendarPage() {
// Extract mission calendars
const missionCalendars = missionUserRelations.flatMap(mu => mu.mission.calendars);
// Debug: Log calendar filtering
console.log('[AGENDA] Calendar filtering:', {
personalCalendarsCount: personalCalendars.length,
personalCalendars: personalCalendars.map(cal => ({
id: cal.id,
name: cal.name,
hasSyncConfig: !!cal.syncConfig,
syncEnabled: cal.syncConfig?.syncEnabled,
provider: cal.syncConfig?.provider,
})),
missionCalendarsCount: missionCalendars.length,
});
// Combine personal and mission calendars
let calendars = [...personalCalendars, ...missionCalendars];

View File

@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/options";
import { prisma } from "@/lib/prisma";
import { updateMicrosoftEvent } from "@/lib/services/microsoft-calendar-sync";
import { logger } from "@/lib/logger";
// Helper function to check if user can manage events in a mission calendar
async function canManageMissionCalendar(userId: string, calendarId: string): Promise<boolean> {
@ -130,7 +132,7 @@ export async function PUT(req: NextRequest) {
);
}
// Verify calendar ownership
// Verify calendar ownership and get event with sync config
const calendar = await prisma.calendar.findFirst({
where: {
id: calendarId,
@ -141,6 +143,11 @@ export async function PUT(req: NextRequest) {
where: {
id
}
},
syncConfig: {
include: {
mailCredential: true
}
}
}
});
@ -169,6 +176,9 @@ export async function PUT(req: NextRequest) {
}
}
const existingEvent = calendar.events[0];
// Update event in local database
const event = await prisma.event.update({
where: { id },
data: {
@ -182,6 +192,86 @@ export async function PUT(req: NextRequest) {
},
});
// If event has externalEventId and calendar has Microsoft sync, update Microsoft too
if (existingEvent.externalEventId && calendar.syncConfig && calendar.syncConfig.provider === 'microsoft' && calendar.syncConfig.syncEnabled) {
const syncConfig = calendar.syncConfig;
const mailCredential = syncConfig.mailCredential;
if (mailCredential && mailCredential.use_oauth && mailCredential.refresh_token) {
try {
// Prepare Microsoft event data
const startDate = new Date(start);
const endDate = new Date(end);
// Microsoft Graph API expects timezone-aware dates
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const microsoftEventData: any = {
subject: title,
};
if (allDay) {
// For all-day events, Microsoft uses date format
microsoftEventData.isAllDay = true;
microsoftEventData.start = {
date: startDate.toISOString().split('T')[0],
timeZone: 'UTC'
};
microsoftEventData.end = {
date: endDate.toISOString().split('T')[0],
timeZone: 'UTC'
};
} else {
// For timed events, use dateTime
microsoftEventData.isAllDay = false;
microsoftEventData.start = {
dateTime: startDate.toISOString(),
timeZone: timeZone
};
microsoftEventData.end = {
dateTime: endDate.toISOString(),
timeZone: timeZone
};
}
if (description) {
microsoftEventData.body = {
contentType: 'HTML',
content: description
};
}
if (location) {
microsoftEventData.location = {
displayName: location
};
}
// Update Microsoft event
await updateMicrosoftEvent(
session.user.id,
mailCredential.email,
syncConfig.externalCalendarId || '',
existingEvent.externalEventId,
microsoftEventData
);
logger.info('Successfully synced event update to Microsoft', {
eventId: id,
externalEventId: existingEvent.externalEventId,
email: mailCredential.email,
});
} catch (syncError: any) {
// Log error but don't fail the request - local update succeeded
logger.error('Failed to sync event update to Microsoft', {
eventId: id,
externalEventId: existingEvent.externalEventId,
error: syncError instanceof Error ? syncError.message : String(syncError),
});
}
}
}
console.log("Updated event:", event);
return NextResponse.json(event);
} catch (error) {

View File

@ -377,6 +377,77 @@ export function convertMicrosoftEventToCalDAV(microsoftEvent: MicrosoftEvent): {
};
}
/**
* Update a Microsoft calendar event via Graph API
*/
export async function updateMicrosoftEvent(
userId: string,
email: string,
calendarId: string,
eventId: string,
eventData: {
subject?: string;
body?: string;
start?: { dateTime: string; timeZone: string };
end?: { dateTime: string; timeZone: string };
location?: { displayName: string };
isAllDay?: boolean;
}
): Promise<void> {
try {
const accessToken = await getMicrosoftGraphClient(userId, email);
// Build the update payload
const payload: any = {};
if (eventData.subject !== undefined) payload.subject = eventData.subject;
if (eventData.body !== undefined) {
payload.body = {
contentType: 'HTML',
content: eventData.body,
};
}
if (eventData.start) payload.start = eventData.start;
if (eventData.end) payload.end = eventData.end;
if (eventData.location) payload.location = eventData.location;
if (eventData.isAllDay !== undefined) payload.isAllDay = eventData.isAllDay;
const url = `https://graph.microsoft.com/v1.0/me/calendars/${calendarId}/events/${eventId}`;
logger.info('Updating Microsoft event', {
userId,
email,
calendarId,
eventId,
url,
payload: JSON.stringify(payload),
});
await axios.patch(url, payload, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
});
logger.info('Successfully updated Microsoft event', {
userId,
email,
eventId,
});
} catch (error: any) {
logger.error('Error updating Microsoft event', {
userId,
email,
calendarId,
eventId,
error: error instanceof Error ? error.message : String(error),
responseStatus: error.response?.status,
responseData: error.response?.data,
});
throw error;
}
}
/**
* Sync events from Microsoft calendar to local Prisma calendar
*/