Agenda refactor
This commit is contained in:
parent
6fefc74fd5
commit
f9e8409ab8
@ -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];
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user